diff -Nru snapd-2.28.5/apparmor/probe.go snapd-2.29.3/apparmor/probe.go --- snapd-2.28.5/apparmor/probe.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/apparmor/probe.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,151 +0,0 @@ -// -*- Mode: Go; indent-tabs-mode: t -*- - -/* - * Copyright (C) 2017 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 . - * - */ - -package apparmor - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "sort" - "strings" -) - -// FeatureLevel encodes the kind of support for apparmor found on this system. -type FeatureLevel int - -const ( - // None indicates that apparmor is not enabled. - None FeatureLevel = iota - // Partial indicates that apparmor is enabled but some features are missing. - Partial - // Full indicates that all features are supported. - Full -) - -var ( - // featureSysPath points to the sysfs directory where apparmor features are listed. - featuresSysPath = "/sys/kernel/security/apparmor/features" - // requiredFeatures are the apparmor features needed for strict confinement. - requiredFeatures = []string{ - "caps", - "dbus", - "domain", - "file", - "mount", - "namespaces", - "network", - "ptrace", - "rlimit", - "signal", - } -) - -// KernelSupport describes apparmor features supported by the kernel. -type KernelSupport struct { - enabled bool - features map[string]bool -} - -// ProbeKernel checks which apparmor features are available. -func ProbeKernel() *KernelSupport { - entries, err := ioutil.ReadDir(featuresSysPath) - if err != nil { - return nil - } - ks := &KernelSupport{ - enabled: err == nil, - features: make(map[string]bool, len(entries)), - } - for _, entry := range entries { - // Each sub-directory represents a speicfic feature. Some have more - // details as additional sub-directories or files therein but we are - // not inspecting that at the moment. - if entry.IsDir() { - ks.features[entry.Name()] = true - } - } - return ks -} - -// IsEnabled returns true if apparmor is enabled. -func (ks *KernelSupport) IsEnabled() bool { - return ks != nil && ks.enabled -} - -// SupportsFeature returns true if a given apparmor feature is supported. -func (ks *KernelSupport) SupportsFeature(feature string) bool { - return ks != nil && ks.features[feature] -} - -// Evaluate checks if the apparmor module is enabled and if all the required features are available. -func (ks *KernelSupport) Evaluate() (level FeatureLevel, summary string) { - if !ks.IsEnabled() { - return None, fmt.Sprintf("apparmor is not enabled") - } - var missing []string - for _, feature := range requiredFeatures { - if !ks.SupportsFeature(feature) { - missing = append(missing, feature) - } - } - if len(missing) > 0 { - sort.Strings(missing) - return Partial, fmt.Sprintf("apparmor is enabled but some features are missing: %s", strings.Join(missing, ", ")) - } - return Full, "apparmor is enabled and all features are available" -} - -// MockFeatureLevel fakes the desired apparmor feature level. -func MockFeatureLevel(level FeatureLevel) (restore func()) { - oldFeaturesSysPath := featuresSysPath - - temp, err := ioutil.TempDir("", "mock-apparmor-feature-level") - if err != nil { - panic(err) - } - featuresSysPath = filepath.Join(temp, "features") - - switch level { - case None: - // create no directory at all (apparmor not available). - case Partial: - // create several feature directories, matching vanilla 4.12 kernel. - for _, feature := range []string{"caps", "domain", "file", "network", "policy", "rlimit"} { - if err := os.MkdirAll(filepath.Join(featuresSysPath, feature), 0755); err != nil { - panic(err) - } - } - case Full: - // create all the feature directories, matching Ubuntu kernels. - for _, feature := range requiredFeatures { - if err := os.MkdirAll(filepath.Join(featuresSysPath, feature), 0755); err != nil { - panic(err) - } - } - } - - return func() { - if err := os.RemoveAll(temp); err != nil { - panic(err) - } - featuresSysPath = oldFeaturesSysPath - } -} diff -Nru snapd-2.28.5/apparmor/probe_test.go snapd-2.29.3/apparmor/probe_test.go --- snapd-2.28.5/apparmor/probe_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/apparmor/probe_test.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -// -*- Mode: Go; indent-tabs-mode: t -*- - -/* - * Copyright (C) 2017 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 . - * - */ - -package apparmor_test - -import ( - . "gopkg.in/check.v1" - "testing" - - "github.com/snapcore/snapd/apparmor" -) - -func Test(t *testing.T) { - TestingT(t) -} - -type probeSuite struct{} - -var _ = Suite(&probeSuite{}) - -func (s *probeSuite) TestMockProbeNone(c *C) { - restore := apparmor.MockFeatureLevel(apparmor.None) - defer restore() - - ks := apparmor.ProbeKernel() - c.Assert(ks.IsEnabled(), Equals, false) - c.Assert(ks.SupportsFeature("dbus"), Equals, false) - c.Assert(ks.SupportsFeature("file"), Equals, false) - - level, summary := ks.Evaluate() - c.Assert(level, Equals, apparmor.None) - c.Assert(summary, Equals, "apparmor is not enabled") -} - -func (s *probeSuite) TestMockProbePartial(c *C) { - restore := apparmor.MockFeatureLevel(apparmor.Partial) - defer restore() - - ks := apparmor.ProbeKernel() - c.Assert(ks.IsEnabled(), Equals, true) - c.Assert(ks.SupportsFeature("dbus"), Equals, false) - c.Assert(ks.SupportsFeature("file"), Equals, true) - - level, summary := ks.Evaluate() - c.Assert(level, Equals, apparmor.Partial) - c.Assert(summary, Equals, "apparmor is enabled but some features are missing: dbus, mount, namespaces, ptrace, signal") -} - -func (s *probeSuite) TestMockProbeFull(c *C) { - restore := apparmor.MockFeatureLevel(apparmor.Full) - defer restore() - - ks := apparmor.ProbeKernel() - c.Assert(ks.IsEnabled(), Equals, true) - c.Assert(ks.SupportsFeature("dbus"), Equals, true) - c.Assert(ks.SupportsFeature("file"), Equals, true) - - level, summary := ks.Evaluate() - c.Assert(level, Equals, apparmor.Full) - c.Assert(summary, Equals, "apparmor is enabled and all features are available") -} diff -Nru snapd-2.28.5/asserts/account_key.go snapd-2.29.3/asserts/account_key.go --- snapd-2.28.5/asserts/account_key.go 2016-11-24 09:36:03.000000000 +0000 +++ snapd-2.29.3/asserts/account_key.go 2017-10-23 06:17:27.000000000 +0000 @@ -102,7 +102,7 @@ _, err := db.Find(AccountType, map[string]string{ "account-id": ak.AccountID(), }) - if err == ErrNotFound { + if IsNotFound(err) { return fmt.Errorf("account-key assertion for %q does not have a matching account assertion", ak.AccountID()) } if err != nil { @@ -119,7 +119,7 @@ "account-id": ak.AccountID(), "name": ak.Name(), }) - if err != nil && err != ErrNotFound { + if err != nil && !IsNotFound(err) { return err } for _, assertion := range assertions { @@ -227,7 +227,7 @@ _, err := db.Find(AccountType, map[string]string{ "account-id": akr.AccountID(), }) - if err == ErrNotFound { + if IsNotFound(err) { return fmt.Errorf("account-key-request assertion for %q does not have a matching account assertion", akr.AccountID()) } if err != nil { diff -Nru snapd-2.28.5/asserts/asserts.go snapd-2.29.3/asserts/asserts.go --- snapd-2.28.5/asserts/asserts.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/asserts/asserts.go 2017-10-23 06:17:27.000000000 +0000 @@ -153,6 +153,39 @@ return formatnum, nil } +// HeadersFromPrimaryKey constructs a headers mapping from the +// primaryKey values and the assertion type, it errors if primaryKey +// has the wrong length. +func HeadersFromPrimaryKey(assertType *AssertionType, primaryKey []string) (headers map[string]string, err error) { + if len(primaryKey) != len(assertType.PrimaryKey) { + return nil, fmt.Errorf("primary key has wrong length for %q assertion", assertType.Name) + } + headers = make(map[string]string, len(assertType.PrimaryKey)) + for i, name := range assertType.PrimaryKey { + keyVal := primaryKey[i] + if keyVal == "" { + return nil, fmt.Errorf("primary key %q header cannot be empty", name) + } + headers[name] = keyVal + } + return headers, nil +} + +// PrimaryKeyFromHeaders extracts the tuple of values from headers +// corresponding to a primary key under the assertion type, it errors +// if there are missing primary key headers. +func PrimaryKeyFromHeaders(assertType *AssertionType, headers map[string]string) (primaryKey []string, err error) { + primaryKey = make([]string, len(assertType.PrimaryKey)) + for i, k := range assertType.PrimaryKey { + keyVal := headers[k] + if keyVal == "" { + return nil, fmt.Errorf("must provide primary key: %v", k) + } + primaryKey[i] = keyVal + } + return primaryKey, nil +} + // Ref expresses a reference to an assertion. type Ref struct { Type *AssertionType @@ -184,13 +217,10 @@ // Resolve resolves the reference using the given find function. func (ref *Ref) Resolve(find func(assertType *AssertionType, headers map[string]string) (Assertion, error)) (Assertion, error) { - if len(ref.PrimaryKey) != len(ref.Type.PrimaryKey) { + headers, err := HeadersFromPrimaryKey(ref.Type, ref.PrimaryKey) + if err != nil { return nil, fmt.Errorf("%q assertion reference primary key has the wrong length (expected %v): %v", ref.Type.Name, ref.Type.PrimaryKey, ref.PrimaryKey) } - headers := make(map[string]string, len(ref.PrimaryKey)) - for i, name := range ref.Type.PrimaryKey { - headers[name] = ref.PrimaryKey[i] - } return find(ref.Type, headers) } diff -Nru snapd-2.28.5/asserts/assertstest/assertstest.go snapd-2.29.3/asserts/assertstest/assertstest.go --- snapd-2.28.5/asserts/assertstest/assertstest.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/asserts/assertstest/assertstest.go 2017-10-23 06:17:27.000000000 +0000 @@ -415,7 +415,7 @@ "account-id": ss.AuthorityID, "public-key-sha3-384": keyID, }) - if err == asserts.ErrNotFound { + if asserts.IsNotFound(err) { return nil } if err != nil { diff -Nru snapd-2.28.5/asserts/asserts_test.go snapd-2.29.3/asserts/asserts_test.go --- snapd-2.28.5/asserts/asserts_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/asserts/asserts_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -77,6 +77,40 @@ c.Check(fmtnum, Equals, 0) } +func (as *assertsSuite) TestPrimaryKeyHelpers(c *C) { + headers, err := asserts.HeadersFromPrimaryKey(asserts.TestOnlyType, []string{"one"}) + c.Assert(err, IsNil) + c.Check(headers, DeepEquals, map[string]string{ + "primary-key": "one", + }) + + headers, err = asserts.HeadersFromPrimaryKey(asserts.TestOnly2Type, []string{"bar", "baz"}) + c.Assert(err, IsNil) + c.Check(headers, DeepEquals, map[string]string{ + "pk1": "bar", + "pk2": "baz", + }) + + _, err = asserts.HeadersFromPrimaryKey(asserts.TestOnly2Type, []string{"bar"}) + c.Check(err, ErrorMatches, `primary key has wrong length for "test-only-2" assertion`) + + _, err = asserts.HeadersFromPrimaryKey(asserts.TestOnly2Type, []string{"", "baz"}) + c.Check(err, ErrorMatches, `primary key "pk1" header cannot be empty`) + + pk, err := asserts.PrimaryKeyFromHeaders(asserts.TestOnly2Type, headers) + c.Assert(err, IsNil) + c.Check(pk, DeepEquals, []string{"bar", "baz"}) + + headers["other"] = "foo" + pk1, err := asserts.PrimaryKeyFromHeaders(asserts.TestOnly2Type, headers) + c.Assert(err, IsNil) + c.Check(pk1, DeepEquals, pk) + + delete(headers, "pk2") + _, err = asserts.PrimaryKeyFromHeaders(asserts.TestOnly2Type, headers) + c.Check(err, ErrorMatches, `must provide primary key: pk2`) +} + func (as *assertsSuite) TestRef(c *C) { ref := &asserts.Ref{ Type: asserts.TestOnly2Type, diff -Nru snapd-2.28.5/asserts/database.go snapd-2.29.3/asserts/database.go --- snapd-2.28.5/asserts/database.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/asserts/database.go 2017-10-23 06:17:27.000000000 +0000 @@ -22,12 +22,33 @@ package asserts import ( - "errors" "fmt" "regexp" "time" ) +// NotFoundError is returned when an assertion can not be found. +type NotFoundError struct { + Type *AssertionType + Headers map[string]string +} + +func (e *NotFoundError) Error() string { + pk, err := PrimaryKeyFromHeaders(e.Type, e.Headers) + if err != nil || len(e.Headers) != len(pk) { + // TODO: worth conveying more information? + return fmt.Sprintf("%s assertion not found", e.Type.Name) + } + + return fmt.Sprintf("%v not found", &Ref{Type: e.Type, PrimaryKey: pk}) +} + +// IsNotFound returns whether err is an assertion not found error. +func IsNotFound(err error) bool { + _, ok := err.(*NotFoundError) + return ok +} + // A Backstore stores assertions. It can store and retrieve assertions // by type under unique primary key headers (whose names are available // from assertType.PrimaryKey). Plus it supports searching by headers. @@ -37,8 +58,9 @@ // It is responsible for checking that assert is newer than a // previously stored revision with the same primary key headers. Put(assertType *AssertionType, assert Assertion) error - // Get returns the assertion with the given unique key for its primary key headers. - // If none is present it returns ErrNotFound. + // Get returns the assertion with the given unique key for its + // primary key headers. If none is present it returns a + // NotFoundError, usually with omitted Headers. Get(assertType *AssertionType, key []string, maxFormat int) (Assertion, error) // Search returns assertions matching the given headers. // It invokes foundCb for each found assertion. @@ -52,7 +74,7 @@ } func (nbs nullBackstore) Get(t *AssertionType, k []string, maxFormat int) (Assertion, error) { - return nil, ErrNotFound + return nil, &NotFoundError{Type: t} } func (nbs nullBackstore) Search(t *AssertionType, h map[string]string, f func(Assertion), maxFormat int) error { @@ -85,11 +107,6 @@ Checkers []Checker } -// Well-known errors -var ( - ErrNotFound = errors.New("assertion not found") -) - // RevisionError indicates a revision improperly used for an operation. type RevisionError struct { Used, Current int @@ -144,25 +161,25 @@ IsTrustedAccount(accountID string) bool // Find an assertion based on arbitrary headers. // Provided headers must contain the primary key for the assertion type. - // It returns ErrNotFound if the assertion cannot be found. + // It returns a NotFoundError if the assertion cannot be found. Find(assertionType *AssertionType, headers map[string]string) (Assertion, error) // FindPredefined finds an assertion in the predefined sets // (trusted or not) based on arbitrary headers. Provided // headers must contain the primary key for the assertion - // type. It returns ErrNotFound if the assertion cannot be - // found. + // type. It returns a NotFoundError if the assertion cannot + // be found. FindPredefined(assertionType *AssertionType, headers map[string]string) (Assertion, error) // FindTrusted finds an assertion in the trusted set based on // arbitrary headers. Provided headers must contain the - // primary key for the assertion type. It returns ErrNotFound - // if the assertion cannot be found. + // primary key for the assertion type. It returns a + // NotFoundError if the assertion cannot be found. FindTrusted(assertionType *AssertionType, headers map[string]string) (Assertion, error) // FindMany finds assertions based on arbitrary headers. - // It returns ErrNotFound if no assertion can be found. + // It returns a NotFoundError if no assertion can be found. FindMany(assertionType *AssertionType, headers map[string]string) ([]Assertion, error) // FindManyPredefined finds assertions in the predefined sets - // (trusted or not) based on arbitrary headers. It returns - // ErrNotFound if no assertion can be found. + // (trusted or not) based on arbitrary headers. It returns a + // NotFoundError if no assertion can be found. FindManyPredefined(assertionType *AssertionType, headers map[string]string) ([]Assertion, error) // Check tests whether the assertion is properly signed and consistent with all the stored knowledge. Check(assert Assertion) error @@ -299,11 +316,11 @@ } return hit, nil } - if err != ErrNotFound { + if !IsNotFound(err) { return nil, err } } - return nil, ErrNotFound + return nil, &NotFoundError{Type: AccountKeyType} } // IsTrustedAccount returns whether the account is part of the trusted set. @@ -329,7 +346,7 @@ if typ.flags&noAuthority == 0 { // TODO: later may need to consider type of assert to find candidate keys accKey, err = db.findAccountKey(assert.AuthorityID(), assert.SignKeyID()) - if err == ErrNotFound { + if IsNotFound(err) { return fmt.Errorf("no matching public key %q for signature by %q", assert.SignKeyID(), assert.AuthorityID()) } if err != nil { @@ -364,7 +381,7 @@ if err != nil { if ufe, ok := err.(*UnsupportedFormatError); ok { _, err := ref.Resolve(db.Find) - if err != nil && err != ErrNotFound { + if err != nil && !IsNotFound(err) { return err } return &UnsupportedFormatError{Ref: ufe.Ref, Format: ufe.Format, Update: err == nil} @@ -382,12 +399,12 @@ // through the os snap this seems the safest policy until we // know more/better _, err = db.trusted.Get(ref.Type, ref.PrimaryKey, ref.Type.MaxSupportedFormat()) - if err != ErrNotFound { + if !IsNotFound(err) { return fmt.Errorf("cannot add %q assertion with primary key clashing with a trusted assertion: %v", ref.Type.Name, ref.PrimaryKey) } _, err = db.predefined.Get(ref.Type, ref.PrimaryKey, ref.Type.MaxSupportedFormat()) - if err != ErrNotFound { + if !IsNotFound(err) { return fmt.Errorf("cannot add %q assertion with primary key clashing with a predefined assertion: %v", ref.Type.Name, ref.PrimaryKey) } @@ -417,13 +434,10 @@ return nil, fmt.Errorf("cannot find %q assertions for format %d higher than supported format %d", assertionType.Name, maxFormat, maxSupp) } } - keyValues := make([]string, len(assertionType.PrimaryKey)) - for i, k := range assertionType.PrimaryKey { - keyVal := headers[k] - if keyVal == "" { - return nil, fmt.Errorf("must provide primary key: %v", k) - } - keyValues[i] = keyVal + + keyValues, err := PrimaryKeyFromHeaders(assertionType, headers) + if err != nil { + return nil, err } var assert Assertion @@ -433,13 +447,13 @@ assert = a break } - if err != ErrNotFound { + if !IsNotFound(err) { return nil, err } } if assert == nil || !searchMatch(assert, headers) { - return nil, ErrNotFound + return nil, &NotFoundError{Type: assertionType, Headers: headers} } return assert, nil @@ -447,29 +461,29 @@ // Find an assertion based on arbitrary headers. // Provided headers must contain the primary key for the assertion type. -// It returns ErrNotFound if the assertion cannot be found. +// It returns a NotFoundError if the assertion cannot be found. func (db *Database) Find(assertionType *AssertionType, headers map[string]string) (Assertion, error) { return find(db.backstores, assertionType, headers, -1) } // FindMaxFormat finds an assertion like Find but such that its // format is <= maxFormat by passing maxFormat along to the backend. -// It returns ErrNotFound if such an assertion cannot be found. +// It returns a NotFoundError if such an assertion cannot be found. func (db *Database) FindMaxFormat(assertionType *AssertionType, headers map[string]string, maxFormat int) (Assertion, error) { return find(db.backstores, assertionType, headers, maxFormat) } // FindPredefined finds an assertion in the predefined sets (trusted // or not) based on arbitrary headers. Provided headers must contain -// the primary key for the assertion type. It returns ErrNotFound if -// the assertion cannot be found. +// the primary key for the assertion type. It returns a NotFoundError +// if the assertion cannot be found. func (db *Database) FindPredefined(assertionType *AssertionType, headers map[string]string) (Assertion, error) { return find([]Backstore{db.trusted, db.predefined}, assertionType, headers, -1) } // FindTrusted finds an assertion in the trusted set based on arbitrary headers. // Provided headers must contain the primary key for the assertion type. -// It returns ErrNotFound if the assertion cannot be found. +// It returns a NotFoundError if the assertion cannot be found. func (db *Database) FindTrusted(assertionType *AssertionType, headers map[string]string) (Assertion, error) { return find([]Backstore{db.trusted}, assertionType, headers, -1) } @@ -495,20 +509,20 @@ } if len(res) == 0 { - return nil, ErrNotFound + return nil, &NotFoundError{Type: assertionType, Headers: headers} } return res, nil } // FindMany finds assertions based on arbitrary headers. -// It returns ErrNotFound if no assertion can be found. +// It returns a NotFoundError if no assertion can be found. func (db *Database) FindMany(assertionType *AssertionType, headers map[string]string) ([]Assertion, error) { return db.findMany(db.backstores, assertionType, headers) } // FindManyPrefined finds assertions in the predefined sets (trusted -// or not) based on arbitrary headers. It returns ErrNotFound if no -// assertion can be found. +// or not) based on arbitrary headers. It returns a NotFoundError if +// no assertion can be found. func (db *Database) FindManyPredefined(assertionType *AssertionType, headers map[string]string) ([]Assertion, error) { return db.findMany([]Backstore{db.trusted, db.predefined}, assertionType, headers) } diff -Nru snapd-2.28.5/asserts/database_test.go snapd-2.29.3/asserts/database_test.go --- snapd-2.28.5/asserts/database_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/asserts/database_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -636,6 +636,24 @@ c.Check(asserts.IsUnaccceptedUpdate(err), Equals, true) } +func (safs *signAddFindSuite) TestNotFoundError(c *C) { + err1 := &asserts.NotFoundError{ + Type: asserts.SnapDeclarationType, + Headers: map[string]string{ + "series": "16", + "snap-id": "snap-id", + }, + } + c.Check(asserts.IsNotFound(err1), Equals, true) + c.Check(err1.Error(), Equals, "snap-declaration (snap-id; series:16) not found") + + err2 := &asserts.NotFoundError{ + Type: asserts.SnapRevisionType, + } + c.Check(asserts.IsNotFound(err1), Equals, true) + c.Check(err2.Error(), Equals, "snap-revision assertion not found") +} + func (safs *signAddFindSuite) TestFindNotFound(c *C) { headers := map[string]interface{}{ "authority-id": "canonical", @@ -647,18 +665,26 @@ err = safs.db.Add(a1) c.Assert(err, IsNil) - retrieved1, err := safs.db.Find(asserts.TestOnlyType, map[string]string{ + hdrs := map[string]string{ "primary-key": "b", + } + retrieved1, err := safs.db.Find(asserts.TestOnlyType, hdrs) + c.Assert(err, DeepEquals, &asserts.NotFoundError{ + Type: asserts.TestOnlyType, + Headers: hdrs, }) - c.Assert(err, Equals, asserts.ErrNotFound) c.Check(retrieved1, IsNil) // checking also extra headers - retrieved1, err = safs.db.Find(asserts.TestOnlyType, map[string]string{ + hdrs = map[string]string{ "primary-key": "a", "authority-id": "other-auth-id", + } + retrieved1, err = safs.db.Find(asserts.TestOnlyType, hdrs) + c.Assert(err, DeepEquals, &asserts.NotFoundError{ + Type: asserts.TestOnlyType, + Headers: hdrs, }) - c.Assert(err, Equals, asserts.ErrNotFound) c.Check(retrieved1, IsNil) } @@ -726,12 +752,16 @@ c.Assert(err, IsNil) c.Assert(res, HasLen, 1) - res, err = safs.db.FindMany(asserts.TestOnlyType, map[string]string{ + hdrs := map[string]string{ "primary-key": "b", "other": "other-x", - }) + } + res, err = safs.db.FindMany(asserts.TestOnlyType, hdrs) c.Assert(res, HasLen, 0) - c.Check(err, Equals, asserts.ErrNotFound) + c.Check(err, DeepEquals, &asserts.NotFoundError{ + Type: asserts.TestOnlyType, + Headers: hdrs, + }) } func (safs *signAddFindSuite) TestFindFindsPredefined(c *C) { @@ -810,21 +840,29 @@ c.Assert(tKey.(*asserts.AccountKey).PublicKeyID(), Equals, safs.signingKeyID) // doesn't find not trusted assertions - _, err = safs.db.FindTrusted(asserts.AccountType, map[string]string{ + hdrs := map[string]string{ "account-id": acct1.AccountID(), + } + _, err = safs.db.FindTrusted(asserts.AccountType, hdrs) + c.Check(err, DeepEquals, &asserts.NotFoundError{ + Type: asserts.AccountType, + Headers: hdrs, }) - c.Check(err, Equals, asserts.ErrNotFound) - _, err = safs.db.FindTrusted(asserts.AccountKeyType, map[string]string{ + hdrs = map[string]string{ "account-id": acct1.AccountID(), "public-key-sha3-384": acct1Key.PublicKeyID(), + } + _, err = safs.db.FindTrusted(asserts.AccountKeyType, hdrs) + c.Check(err, DeepEquals, &asserts.NotFoundError{ + Type: asserts.AccountKeyType, + Headers: hdrs, }) - c.Check(err, Equals, asserts.ErrNotFound) _, err = safs.db.FindTrusted(asserts.AccountType, map[string]string{ "account-id": "predefined", }) - c.Check(err, Equals, asserts.ErrNotFound) + c.Check(asserts.IsNotFound(err), Equals, true) } func (safs *signAddFindSuite) TestFindPredefined(c *C) { @@ -868,16 +906,24 @@ c.Assert(predefAcct.(*asserts.Account).DisplayName(), Equals, "Predef") // doesn't find not trusted or predefined assertions - _, err = safs.db.FindPredefined(asserts.AccountType, map[string]string{ + hdrs := map[string]string{ "account-id": acct1.AccountID(), + } + _, err = safs.db.FindPredefined(asserts.AccountType, hdrs) + c.Check(err, DeepEquals, &asserts.NotFoundError{ + Type: asserts.AccountType, + Headers: hdrs, }) - c.Check(err, Equals, asserts.ErrNotFound) - _, err = safs.db.FindPredefined(asserts.AccountKeyType, map[string]string{ + hdrs = map[string]string{ "account-id": acct1.AccountID(), "public-key-sha3-384": acct1Key.PublicKeyID(), + } + _, err = safs.db.FindPredefined(asserts.AccountKeyType, hdrs) + c.Check(err, DeepEquals, &asserts.NotFoundError{ + Type: asserts.AccountKeyType, + Headers: hdrs, }) - c.Check(err, Equals, asserts.ErrNotFound) } func (safs *signAddFindSuite) TestFindManyPredefined(c *C) { @@ -956,16 +1002,20 @@ }) // doesn't find not predefined assertions - _, err = db.FindManyPredefined(asserts.AccountType, map[string]string{ + hdrs := map[string]string{ "account-id": acct1.AccountID(), + } + _, err = db.FindManyPredefined(asserts.AccountType, hdrs) + c.Check(err, DeepEquals, &asserts.NotFoundError{ + Type: asserts.AccountType, + Headers: hdrs, }) - c.Check(err, Equals, asserts.ErrNotFound) _, err = db.FindManyPredefined(asserts.AccountKeyType, map[string]string{ "account-id": acct1.AccountID(), "public-key-sha3-384": acct1Key.PublicKeyID(), }) - c.Check(err, Equals, asserts.ErrNotFound) + c.Check(asserts.IsNotFound(err), Equals, true) } func (safs *signAddFindSuite) TestDontLetAddConfusinglyAssertionClashingWithTrustedOnes(c *C) { @@ -1039,7 +1089,13 @@ PrimaryKey: []string{"kb", "ka"}, } _, err = ref.Resolve(safs.db.Find) - c.Assert(err, Equals, asserts.ErrNotFound) + c.Assert(err, DeepEquals, &asserts.NotFoundError{ + Type: ref.Type, + Headers: map[string]string{ + "pk1": "kb", + "pk2": "ka", + }, + }) } func (safs *signAddFindSuite) TestFindMaxFormat(c *C) { @@ -1125,7 +1181,7 @@ {&asserts.RevisionError{Used: 1, Current: 5}, true}, {&asserts.RevisionError{Used: 3, Current: 1}, false}, {errors.New("other error"), false}, - {asserts.ErrNotFound, false}, + {&asserts.NotFoundError{Type: asserts.TestOnlyType}, false}, } for _, t := range tests { diff -Nru snapd-2.28.5/asserts/fetcher.go snapd-2.29.3/asserts/fetcher.go --- snapd-2.28.5/asserts/fetcher.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/asserts/fetcher.go 2017-10-23 06:17:27.000000000 +0000 @@ -66,7 +66,7 @@ if err == nil { return nil } - if err != ErrNotFound { + if !IsNotFound(err) { return err } u := ref.Unique() diff -Nru snapd-2.28.5/asserts/fsbackstore.go snapd-2.29.3/asserts/fsbackstore.go --- snapd-2.28.5/asserts/fsbackstore.go 2016-10-27 13:22:15.000000000 +0000 +++ snapd-2.29.3/asserts/fsbackstore.go 2017-10-23 06:17:27.000000000 +0000 @@ -55,7 +55,7 @@ func (fsbs *filesystemBackstore) readAssertion(assertType *AssertionType, diskPrimaryPath string) (Assertion, error) { encoded, err := readEntry(fsbs.top, assertType.Name, diskPrimaryPath) if os.IsNotExist(err) { - return nil, ErrNotFound + return nil, errNotFound } if err != nil { return nil, fmt.Errorf("broken assertion storage, cannot read assertion: %v", err) @@ -94,7 +94,7 @@ } } if a == nil { - return nil, ErrNotFound + return nil, errNotFound } return a, nil } @@ -115,7 +115,7 @@ namesCb := func(relpaths []string) error { var err error a, err = fsbs.pickLatestAssertion(assertType, relpaths, maxFormat) - if err == ErrNotFound { + if err == errNotFound { return nil } return err @@ -129,7 +129,7 @@ } if a == nil { - return nil, ErrNotFound + return nil, errNotFound } return a, nil @@ -139,10 +139,7 @@ fsbs.mu.Lock() defer fsbs.mu.Unlock() - primaryPath := make([]string, len(assertType.PrimaryKey)) - for i, k := range assertType.PrimaryKey { - primaryPath[i] = assert.HeaderString(k) - } + primaryPath := assert.Ref().PrimaryKey curAssert, err := fsbs.currentAssertion(assertType, primaryPath, assertType.MaxSupportedFormat()) if err == nil { @@ -151,7 +148,7 @@ if curRev >= rev { return &RevisionError{Current: curRev, Used: rev} } - } else if err != ErrNotFound { + } else if err != errNotFound { return err } @@ -172,14 +169,18 @@ fsbs.mu.RLock() defer fsbs.mu.RUnlock() - return fsbs.currentAssertion(assertType, key, maxFormat) + a, err := fsbs.currentAssertion(assertType, key, maxFormat) + if err == errNotFound { + return nil, &NotFoundError{Type: assertType} + } + return a, err } func (fsbs *filesystemBackstore) search(assertType *AssertionType, diskPattern []string, foundCb func(Assertion), maxFormat int) error { assertTypeTop := filepath.Join(fsbs.top, assertType.Name) candCb := func(diskPrimaryPaths []string) error { a, err := fsbs.pickLatestAssertion(assertType, diskPrimaryPaths, maxFormat) - if err == ErrNotFound { + if err == errNotFound { return nil } if err != nil { diff -Nru snapd-2.28.5/asserts/fsbackstore_test.go snapd-2.29.3/asserts/fsbackstore_test.go --- snapd-2.28.5/asserts/fsbackstore_test.go 2016-10-27 13:22:15.000000000 +0000 +++ snapd-2.29.3/asserts/fsbackstore_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -150,13 +150,20 @@ c.Check(a.Revision(), Equals, 0) a, err = bs.Get(asserts.TestOnlyType, []string{"zoo"}, 0) - c.Assert(err, Equals, asserts.ErrNotFound) + c.Assert(err, DeepEquals, &asserts.NotFoundError{ + Type: asserts.TestOnlyType, + // Headers can be omitted by Backstores + }) + c.Check(a, IsNil) err = bs.Put(asserts.TestOnlyType, af2) c.Assert(err, IsNil) a, err = bs.Get(asserts.TestOnlyType, []string{"zoo"}, 1) - c.Assert(err, Equals, asserts.ErrNotFound) + c.Assert(err, DeepEquals, &asserts.NotFoundError{ + Type: asserts.TestOnlyType, + }) + c.Check(a, IsNil) a, err = bs.Get(asserts.TestOnlyType, []string{"zoo"}, 2) c.Assert(err, IsNil) diff -Nru snapd-2.28.5/asserts/membackstore.go snapd-2.29.3/asserts/membackstore.go --- snapd-2.28.5/asserts/membackstore.go 2016-11-24 09:36:03.000000000 +0000 +++ snapd-2.29.3/asserts/membackstore.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,6 +20,7 @@ package asserts import ( + "errors" "sync" ) @@ -80,11 +81,15 @@ return nil } +// errNotFound is used internally by backends, it is converted to the richer +// NotFoundError only at their public interface boundary +var errNotFound = errors.New("assertion not found") + func (br memBSBranch) get(key []string, maxFormat int) (Assertion, error) { key0 := key[0] down := br[key0] if down == nil { - return nil, ErrNotFound + return nil, errNotFound } return down.get(key[1:], maxFormat) } @@ -93,7 +98,7 @@ key0 := key[0] cur := leaf.cur(key0, maxFormat) if cur == nil { - return nil, ErrNotFound + return nil, errNotFound } return cur, nil } @@ -142,11 +147,9 @@ mbs.mu.Lock() defer mbs.mu.Unlock() - internalKey := make([]string, 1+len(assertType.PrimaryKey)) + internalKey := make([]string, 1, 1+len(assertType.PrimaryKey)) internalKey[0] = assertType.Name - for i, name := range assertType.PrimaryKey { - internalKey[1+i] = assert.HeaderString(name) - } + internalKey = append(internalKey, assert.Ref().PrimaryKey...) err := mbs.top.put(assertType, internalKey, assert) return err @@ -160,7 +163,11 @@ internalKey[0] = assertType.Name copy(internalKey[1:], key) - return mbs.top.get(internalKey, maxFormat) + a, err := mbs.top.get(internalKey, maxFormat) + if err == errNotFound { + return nil, &NotFoundError{Type: assertType} + } + return a, err } func (mbs *memoryBackstore) Search(assertType *AssertionType, headers map[string]string, foundCb func(Assertion), maxFormat int) error { diff -Nru snapd-2.28.5/asserts/membackstore_test.go snapd-2.29.3/asserts/membackstore_test.go --- snapd-2.28.5/asserts/membackstore_test.go 2016-10-27 13:22:15.000000000 +0000 +++ snapd-2.29.3/asserts/membackstore_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -58,14 +58,19 @@ func (mbss *memBackstoreSuite) TestGetNotFound(c *C) { a, err := mbss.bs.Get(asserts.TestOnlyType, []string{"foo"}, 0) - c.Assert(err, Equals, asserts.ErrNotFound) + c.Assert(err, DeepEquals, &asserts.NotFoundError{ + Type: asserts.TestOnlyType, + // Headers can be omitted by Backstores + }) c.Check(a, IsNil) err = mbss.bs.Put(asserts.TestOnlyType, mbss.a) c.Assert(err, IsNil) a, err = mbss.bs.Get(asserts.TestOnlyType, []string{"bar"}, 0) - c.Assert(err, Equals, asserts.ErrNotFound) + c.Assert(err, DeepEquals, &asserts.NotFoundError{ + Type: asserts.TestOnlyType, + }) c.Check(a, IsNil) } @@ -247,13 +252,13 @@ c.Check(a.Revision(), Equals, 0) a, err = bs.Get(asserts.TestOnlyType, []string{"zoo"}, 0) - c.Assert(err, Equals, asserts.ErrNotFound) + c.Assert(err, FitsTypeOf, &asserts.NotFoundError{}) err = bs.Put(asserts.TestOnlyType, af2) c.Assert(err, IsNil) a, err = bs.Get(asserts.TestOnlyType, []string{"zoo"}, 1) - c.Assert(err, Equals, asserts.ErrNotFound) + c.Assert(err, FitsTypeOf, &asserts.NotFoundError{}) a, err = bs.Get(asserts.TestOnlyType, []string{"zoo"}, 2) c.Assert(err, IsNil) diff -Nru snapd-2.28.5/asserts/repair.go snapd-2.29.3/asserts/repair.go --- snapd-2.28.5/asserts/repair.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/asserts/repair.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,7 +20,10 @@ package asserts import ( + "fmt" "regexp" + "strconv" + "strings" "time" ) @@ -33,6 +36,8 @@ architectures []string models []string + id int + disabled bool timestamp time.Time } @@ -42,12 +47,17 @@ return r.HeaderString("brand-id") } -// RepairID returns the "id" of the repair. It should be a short string -// that follows a convention like "REPAIR-123". Similar to a CVE there -// should be a public place to look up details about the repair-id +// RepairID returns the sequential id of the repair. There +// should be a public place to look up details about the repair +// by brand-id and repair-id. // (e.g. the snapcraft forum). -func (r *Repair) RepairID() string { - return r.HeaderString("repair-id") +func (r *Repair) RepairID() int { + return r.id +} + +// Summary returns the mandatory summary description of the repair. +func (r *Repair) Summary() string { + return r.HeaderString("summary") } // Architectures returns the architectures that this assertions applies to. @@ -96,9 +106,23 @@ return nil, err } - if _, err = checkStringMatchesWhat(assert.headers, "repair-id", "header", validRepairID); err != nil { + repairID, err := checkStringMatches(assert.headers, "repair-id", validRepairID) + if err != nil { return nil, err } + id, err := strconv.Atoi(repairID) + if err != nil { + // given it matched it can likely only be too large + return nil, fmt.Errorf("repair-id too large: %s", repairID) + } + + summary, err := checkNotEmptyString(assert.headers, "summary") + if err != nil { + return nil, err + } + if strings.ContainsAny(summary, "\n\r") { + return nil, fmt.Errorf(`"summary" header cannot have newlines`) + } series, err := checkStringList(assert.headers, "series") if err != nil { @@ -128,6 +152,7 @@ series: series, architectures: architectures, models: models, + id: id, disabled: disabled, timestamp: timestamp, }, nil diff -Nru snapd-2.28.5/asserts/repair_test.go snapd-2.29.3/asserts/repair_test.go --- snapd-2.28.5/asserts/repair_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/asserts/repair_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -67,6 +67,7 @@ var repairExample = fmt.Sprintf("type: repair\n"+ "authority-id: acme\n"+ "brand-id: acme\n"+ + "summary: example repair\n"+ "architectures:\n"+ " - amd64\n"+ " - arm64\n"+ @@ -97,7 +98,8 @@ repair := a.(*asserts.Repair) c.Check(repair.Timestamp(), Equals, s.ts) c.Check(repair.BrandID(), Equals, "acme") - c.Check(repair.RepairID(), Equals, "42") + c.Check(repair.RepairID(), Equals, 42) + c.Check(repair.Summary(), Equals, "example repair") c.Check(repair.Series(), DeepEquals, []string{"16"}) c.Check(repair.Architectures(), DeepEquals, []string{"amd64", "arm64"}) c.Check(repair.Models(), DeepEquals, []string{"acme/frobinator"}) @@ -143,7 +145,11 @@ {"repair-id: 42\n", "repair-id: no-number\n", `"repair-id" header contains invalid characters: "no-number"`}, {"repair-id: 42\n", "repair-id: 0\n", `"repair-id" header contains invalid characters: "0"`}, {"repair-id: 42\n", "repair-id: 01\n", `"repair-id" header contains invalid characters: "01"`}, + {"repair-id: 42\n", "repair-id: 99999999999999999999\n", `repair-id too large:.*`}, {"brand-id: acme\n", "brand-id: brand-id-not-eq-authority-id\n", `authority-id and brand-id must match, repair assertions are expected to be signed by the brand: "acme" != "brand-id-not-eq-authority-id"`}, + {"summary: example repair\n", "", `"summary" header is mandatory`}, + {"summary: example repair\n", "summary: \n", `"summary" header should not be empty`}, + {"summary: example repair\n", "summary:\n multi\n line\n", `"summary" header cannot have newlines`}, {s.tsLine, "", `"timestamp" header is mandatory`}, {s.tsLine, "timestamp: \n", `"timestamp" header should not be empty`}, {s.tsLine, "timestamp: 12:30\n", `"timestamp" header is not a RFC3339 date: .*`}, diff -Nru snapd-2.28.5/asserts/snapasserts/snapasserts.go snapd-2.29.3/asserts/snapasserts/snapasserts.go --- snapd-2.28.5/asserts/snapasserts/snapasserts.go 2017-08-18 13:48:10.000000000 +0000 +++ snapd-2.29.3/asserts/snapasserts/snapasserts.go 2017-10-23 06:17:27.000000000 +0000 @@ -29,9 +29,10 @@ ) type Finder interface { - // Find an assertion based on arbitrary headers. - // Provided headers must contain the primary key for the assertion type. - // It returns ErrNotFound if the assertion cannot be found. + // Find an assertion based on arbitrary headers. Provided + // headers must contain the primary key for the assertion + // type. It returns a asserts.NotFoundError if the assertion + // cannot be found. Find(assertionType *asserts.AssertionType, headers map[string]string) (asserts.Assertion, error) } @@ -85,7 +86,7 @@ return nil } -// DeriveSideInfo tries to construct a SideInfo for the given snap using its digest to find the relevant snap assertions with the information in the given database. It will fail with asserts.ErrNotFound if it cannot find them. +// DeriveSideInfo tries to construct a SideInfo for the given snap using its digest to find the relevant snap assertions with the information in the given database. It will fail with an asserts.NotFoundError if it cannot find them. func DeriveSideInfo(snapPath string, db Finder) (*snap.SideInfo, error) { snapSHA3_384, snapSize, err := asserts.SnapFileSHA3_384(snapPath) if err != nil { diff -Nru snapd-2.28.5/asserts/snapasserts/snapasserts_test.go snapd-2.29.3/asserts/snapasserts/snapasserts_test.go --- snapd-2.28.5/asserts/snapasserts/snapasserts_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/asserts/snapasserts/snapasserts_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -247,7 +247,7 @@ _, err = snapasserts.DeriveSideInfo(snapPath, s.localDB) // cannot find signatures with metadata for snap - c.Assert(err, Equals, asserts.ErrNotFound) + c.Assert(asserts.IsNotFound(err), Equals, true) } func (s *snapassertsSuite) TestDeriveSideInfoSizeMismatch(c *C) { diff -Nru snapd-2.28.5/asserts/snap_asserts.go snapd-2.29.3/asserts/snap_asserts.go --- snapd-2.28.5/asserts/snap_asserts.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/asserts/snap_asserts.go 2017-10-23 06:17:27.000000000 +0000 @@ -104,7 +104,7 @@ _, err := db.Find(AccountType, map[string]string{ "account-id": snapdcl.PublisherID(), }) - if err == ErrNotFound { + if IsNotFound(err) { return fmt.Errorf("snap-declaration assertion for %q (id %q) does not have a matching account assertion for the publisher %q", snapdcl.SnapName(), snapdcl.SnapID(), snapdcl.PublisherID()) } if err != nil { @@ -436,7 +436,7 @@ _, err := db.Find(AccountType, map[string]string{ "account-id": snaprev.DeveloperID(), }) - if err == ErrNotFound { + if IsNotFound(err) { return fmt.Errorf("snap-revision assertion for snap id %q does not have a matching account assertion for the developer %q", snaprev.SnapID(), snaprev.DeveloperID()) } if err != nil { @@ -447,7 +447,7 @@ "series": release.Series, "snap-id": snaprev.SnapID(), }) - if err == ErrNotFound { + if IsNotFound(err) { return fmt.Errorf("snap-revision assertion for snap id %q does not have a matching snap-declaration assertion", snaprev.SnapID()) } if err != nil { @@ -557,7 +557,7 @@ "series": validation.Series(), "snap-id": validation.ApprovedSnapID(), }) - if err == ErrNotFound { + if IsNotFound(err) { return fmt.Errorf("validation assertion by snap-id %q does not have a matching snap-declaration assertion for approved-snap-id %q", validation.SnapID(), validation.ApprovedSnapID()) } if err != nil { @@ -567,7 +567,7 @@ "series": validation.Series(), "snap-id": validation.SnapID(), }) - if err == ErrNotFound { + if IsNotFound(err) { return fmt.Errorf("validation assertion by snap-id %q does not have a matching snap-declaration assertion", validation.SnapID()) } if err != nil { @@ -803,7 +803,7 @@ "snap-id": snapdev.SnapID(), }) if err != nil { - if err == ErrNotFound { + if IsNotFound(err) { return fmt.Errorf("snap-developer assertion for snap id %q does not have a matching snap-declaration assertion", snapdev.SnapID()) } return err @@ -812,7 +812,7 @@ // check there's an account for the publisher-id _, err = db.Find(AccountType, map[string]string{"account-id": publisherID}) if err != nil { - if err == ErrNotFound { + if IsNotFound(err) { return fmt.Errorf("snap-developer assertion for snap-id %q does not have a matching account assertion for the publisher %q", snapdev.SnapID(), publisherID) } return err @@ -825,7 +825,7 @@ } _, err = db.Find(AccountType, map[string]string{"account-id": developerID}) if err != nil { - if err == ErrNotFound { + if IsNotFound(err) { return fmt.Errorf("snap-developer assertion for snap-id %q does not have a matching account assertion for the developer %q", snapdev.SnapID(), developerID) } return err diff -Nru snapd-2.28.5/asserts/store_asserts.go snapd-2.29.3/asserts/store_asserts.go --- snapd-2.28.5/asserts/store_asserts.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/asserts/store_asserts.go 2017-10-23 06:17:27.000000000 +0000 @@ -1,3 +1,22 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + package asserts import ( @@ -41,7 +60,7 @@ _, err := db.Find(AccountType, map[string]string{"account-id": store.OperatorID()}) if err != nil { - if err == ErrNotFound { + if IsNotFound(err) { return fmt.Errorf( "store assertion %q does not have a matching account assertion for the operator %q", store.Store(), store.OperatorID()) diff -Nru snapd-2.28.5/asserts/store_asserts_test.go snapd-2.29.3/asserts/store_asserts_test.go --- snapd-2.28.5/asserts/store_asserts_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/asserts/store_asserts_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -1,3 +1,22 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + package asserts_test import ( diff -Nru snapd-2.28.5/client/interfaces.go snapd-2.29.3/client/interfaces.go --- snapd-2.28.5/client/interfaces.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/client/interfaces.go 2017-10-23 06:17:27.000000000 +0000 @@ -98,7 +98,7 @@ Connected bool } -func (client *Client) Interfaces(opts *InterfaceOptions) (interfaces []*Interface, err error) { +func (client *Client) Interfaces(opts *InterfaceOptions) ([]*Interface, error) { query := url.Values{} if opts != nil && len(opts.Names) > 0 { query.Set("names", strings.Join(opts.Names, ",")) // Return just those specific interfaces. @@ -120,8 +120,10 @@ } else { query.Set("select", "all") // Return all interfaces. } - _, err = client.doSync("GET", "/v2/interfaces", query, nil, nil, &interfaces) - return + var interfaces []*Interface + _, err := client.doSync("GET", "/v2/interfaces", query, nil, nil, &interfaces) + + return interfaces, err } // performInterfaceAction performs a single action on the interface system. diff -Nru snapd-2.28.5/cmd/autogen.sh snapd-2.29.3/cmd/autogen.sh --- snapd-2.28.5/cmd/autogen.sh 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/cmd/autogen.sh 2017-10-23 06:17:27.000000000 +0000 @@ -20,7 +20,7 @@ . /etc/os-release case "$ID" in arch) - extra_opts="--libexecdir=/usr/lib/snapd --with-snap-mount-dir=/var/lib/snapd/snap --disable-apparmor --enable-nvidia-arch --enable-merged-usr" + extra_opts="--libexecdir=/usr/lib/snapd --with-snap-mount-dir=/var/lib/snapd/snap --disable-apparmor --enable-nvidia-biarch --enable-merged-usr" ;; debian) extra_opts="--libexecdir=/usr/lib/snapd" @@ -28,10 +28,10 @@ ubuntu) case "$VERSION_ID" in 16.04) - extra_opts="--libexecdir=/usr/lib/snapd --enable-nvidia-ubuntu --enable-static-libcap --enable-static-libapparmor --enable-static-libseccomp" + extra_opts="--libexecdir=/usr/lib/snapd --enable-nvidia-multiarch --enable-static-libcap --enable-static-libapparmor --enable-static-libseccomp" ;; *) - extra_opts="--libexecdir=/usr/lib/snapd --enable-nvidia-ubuntu --enable-static-libcap" + extra_opts="--libexecdir=/usr/lib/snapd --enable-nvidia-multiarch --enable-static-libcap" ;; esac ;; @@ -39,11 +39,10 @@ extra_opts="--libexecdir=/usr/libexec/snapd --with-snap-mount-dir=/var/lib/snapd/snap --enable-merged-usr --disable-apparmor" ;; opensuse) - # NOTE: we need to disable apparmor as the version on OpenSUSE - # is too old to confine snap-confine and installed snaps - # themselves. This should be changed once all the kernel - # patches find their way into the distribution. - extra_opts="--libexecdir=/usr/lib/snapd --disable-apparmor" + extra_opts="--libexecdir=/usr/lib/snapd" + ;; + solus) + extra_opts="--enable-nvidia-biarch" ;; esac diff -Nru snapd-2.28.5/cmd/cmd.go snapd-2.29.3/cmd/cmd.go --- snapd-2.28.5/cmd/cmd.go 2017-10-10 16:16:09.000000000 +0000 +++ snapd-2.29.3/cmd/cmd.go 2017-11-09 11:33:41.000000000 +0000 @@ -176,6 +176,15 @@ // Did we already re-exec? if strings.HasPrefix(exe, dirs.SnapMountDir) { + // Older version of snapd (before 2.28) did use this env + // to check if they should re-exec or not. We still need + // to unset it because the host snap tool may be old and + // using this key. So if e.g. the host has snapd 2.27 and + // snapd re-execs to 2.29 then `snap run --shell classic-snap` + // will go into an environment where the snapd 2.27 sees + // this key and stops re-execing - which is not what we + // want. C.f. https://forum.snapcraft.io/t/seccomp-error-calling-snap-from-another-classic-snap-on-core-candidate/2736/7 + mustUnsetenv("SNAP_DID_REEXEC") return } diff -Nru snapd-2.28.5/cmd/cmd_test.go snapd-2.29.3/cmd/cmd_test.go --- snapd-2.28.5/cmd/cmd_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/cmd_test.go 2017-11-09 11:33:41.000000000 +0000 @@ -30,6 +30,7 @@ "github.com/snapcore/snapd/cmd" "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/testutil" ) @@ -38,6 +39,7 @@ type cmdSuite struct { restoreExec func() + restoreLogger func() execCalled int lastExecArgv0 string lastExecArgv []string @@ -51,6 +53,7 @@ func (s *cmdSuite) SetUpTest(c *C) { s.restoreExec = cmd.MockSyscallExec(s.syscallExec) + _, s.restoreLogger = logger.MockLogger() s.execCalled = 0 s.lastExecArgv0 = "" s.lastExecArgv = nil @@ -64,6 +67,7 @@ func (s *cmdSuite) TearDownTest(c *C) { s.restoreExec() + s.restoreLogger() } func (s *cmdSuite) syscallExec(argv0 string, argv []string, envv []string) (err error) { @@ -289,6 +293,7 @@ selfExe := filepath.Join(s.fakeroot, "proc/self/exe") err := os.Symlink(filepath.Join(s.fakeroot, "/snap/core/42/usr/lib/snapd"), selfExe) c.Assert(err, IsNil) + cmd.MockSelfExe(selfExe) cmd.ExecInCoreSnap() c.Check(s.execCalled, Equals, 0) @@ -303,3 +308,17 @@ cmd.ExecInCoreSnap() c.Check(s.execCalled, Equals, 0) } + +func (s *cmdSuite) TestExecInCoreSnapUnsetsDidReexec(c *C) { + os.Setenv("SNAP_DID_REEXEC", "1") + defer os.Unsetenv("SNAP_DID_REEXEC") + + selfExe := filepath.Join(s.fakeroot, "proc/self/exe") + err := os.Symlink(filepath.Join(s.fakeroot, "/snap/core/42/usr/lib/snapd"), selfExe) + c.Assert(err, IsNil) + cmd.MockSelfExe(selfExe) + + cmd.ExecInCoreSnap() + c.Check(s.execCalled, Equals, 0) + c.Check(os.Getenv("SNAP_DID_REEXEC"), Equals, "") +} diff -Nru snapd-2.28.5/cmd/configure.ac snapd-2.29.3/cmd/configure.ac --- snapd-2.28.5/cmd/configure.ac 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/cmd/configure.ac 2017-10-23 06:17:27.000000000 +0000 @@ -114,32 +114,32 @@ # PKG_CHECK_MODULES([LIBCAP], [libcap]) # Enable special support for hosts with proprietary nvidia drivers on Ubuntu. -AC_ARG_ENABLE([nvidia-ubuntu], - AS_HELP_STRING([--enable-nvidia-ubuntu], [Support for proprietary nvidia drivers (Ubuntu)]), +AC_ARG_ENABLE([nvidia-multiarch], + AS_HELP_STRING([--enable-nvidia-multiarch], [Support for proprietary nvidia drivers (Ubuntu/Debian)]), [case "${enableval}" in - yes) enable_nvidia_ubuntu=yes ;; - no) enable_nvidia_ubuntu=no ;; - *) AC_MSG_ERROR([bad value ${enableval} for --enable-nvidia-ubuntu]) - esac], [enable_nvidia_ubuntu=no]) -AM_CONDITIONAL([NVIDIA_UBUNTU], [test "x$enable_nvidia_ubuntu" = "xyes"]) + yes) enable_nvidia_multiarch=yes ;; + no) enable_nvidia_multiarch=no ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-nvidia-multiarch]) + esac], [enable_nvidia_multiarch=no]) +AM_CONDITIONAL([NVIDIA_MULTIARCH], [test "x$enable_nvidia_multiarch" = "xyes"]) -AS_IF([test "x$enable_nvidia_ubuntu" = "xyes"], [ - AC_DEFINE([NVIDIA_UBUNTU], [1], - [Support for proprietary nvidia drivers (Ubuntu)])]) +AS_IF([test "x$enable_nvidia_multiarch" = "xyes"], [ + AC_DEFINE([NVIDIA_MULTIARCH], [1], + [Support for proprietary nvidia drivers (Ubuntu/Debian)])]) # Enable special support for hosts with proprietary nvidia drivers on Arch. -AC_ARG_ENABLE([nvidia-arch], - AS_HELP_STRING([--enable-nvidia-arch], [Support for proprietary nvidia drivers (Arch)]), +AC_ARG_ENABLE([nvidia-biarch], + AS_HELP_STRING([--enable-nvidia-biarch], [Support for proprietary nvidia drivers (bi-arch distributions)]), [case "${enableval}" in - yes) enable_nvidia_arch=yes ;; - no) enable_nvidia_arch=no ;; - *) AC_MSG_ERROR([bad value ${enableval} for --enable-nvidia-arch]) - esac], [enable_nvidia_arch=no]) -AM_CONDITIONAL([NVIDIA_ARCH], [test "x$enable_nvidia_arch" = "xyes"]) + yes) enable_nvidia_biarch=yes ;; + no) enable_nvidia_biarch=no ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-nvidia-biarch]) + esac], [enable_nvidia_biarch=no]) +AM_CONDITIONAL([NVIDIA_BIARCH], [test "x$enable_nvidia_biarch" = "xyes"]) -AS_IF([test "x$enable_nvidia_arch" = "xyes"], [ - AC_DEFINE([NVIDIA_ARCH], [1], - [Support for proprietary nvidia drivers (Arch)])]) +AS_IF([test "x$enable_nvidia_biarch" = "xyes"], [ + AC_DEFINE([NVIDIA_BIARCH], [1], + [Support for proprietary nvidia drivers (bi-arch distributions)])]) AC_ARG_ENABLE([merged-usr], AS_HELP_STRING([--enable-merged-usr], [Enable support for merged /usr directory]), diff -Nru snapd-2.28.5/cmd/decode-mount-opts/decode-mount-opts.c snapd-2.29.3/cmd/decode-mount-opts/decode-mount-opts.c --- snapd-2.28.5/cmd/decode-mount-opts/decode-mount-opts.c 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/cmd/decode-mount-opts/decode-mount-opts.c 2017-10-23 06:17:27.000000000 +0000 @@ -32,7 +32,7 @@ fprintf(stderr, "cannot parse given argument as a number\n"); return 1; } - char buf[1000]; + char buf[1000] = {0}; printf("%#lx is %s\n", mountflags, sc_mount_opt2str(buf, sizeof buf, mountflags)); return 0; } diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/cgroup-freezer-support.c snapd-2.29.3/cmd/libsnap-confine-private/cgroup-freezer-support.c --- snapd-2.28.5/cmd/libsnap-confine-private/cgroup-freezer-support.c 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/cgroup-freezer-support.c 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,67 @@ +// For AT_EMPTY_PATH and O_PATH +#define _GNU_SOURCE + +#include "cgroup-freezer-support.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "cleanup-funcs.h" +#include "string-utils.h" +#include "utils.h" + +static const char *freezer_cgroup_dir = "/sys/fs/cgroup/freezer"; + +void sc_cgroup_freezer_join(const char *snap_name, pid_t pid) +{ + // Format the name of the cgroup hierarchy. + char buf[PATH_MAX] = { 0 }; + sc_must_snprintf(buf, sizeof buf, "snap.%s", snap_name); + + // Open the freezer cgroup directory. + int cgroup_fd SC_CLEANUP(sc_cleanup_close) = -1; + cgroup_fd = open(freezer_cgroup_dir, + O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (cgroup_fd < 0) { + die("cannot open freezer cgroup (%s)", freezer_cgroup_dir); + } + // Create the freezer hierarchy for the given snap. + if (mkdirat(cgroup_fd, buf, 0755) < 0 && errno != EEXIST) { + die("cannot create freezer cgroup hierarchy for snap %s", + snap_name); + } + // Open the hierarchy directory for the given snap. + int hierarchy_fd SC_CLEANUP(sc_cleanup_close) = -1; + hierarchy_fd = openat(cgroup_fd, buf, + O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (hierarchy_fd < 0) { + die("cannot open freezer cgroup hierarchy for snap %s", + snap_name); + } + // Since we may be running from a setuid but not setgid executable, ensure + // that the group and owner of the hierarchy directory is root.root. + if (fchownat(hierarchy_fd, "", 0, 0, AT_EMPTY_PATH) < 0) { + die("cannot change owner of freezer cgroup hierarchy for snap %s to root.root", snap_name); + } + // Open the tasks file. + int tasks_fd SC_CLEANUP(sc_cleanup_close) = -1; + tasks_fd = openat(hierarchy_fd, "tasks", + O_WRONLY | O_NOFOLLOW | O_CLOEXEC); + if (tasks_fd < 0) { + die("cannot open tasks file for freezer cgroup hierarchy for snap %s", snap_name); + } + // Write the process (task) number to the tasks file. Linux task IDs are + // limited to 2^29 so a long int is enough to represent it. + // See include/linux/threads.h in the kernel source tree for details. + int n = sc_must_snprintf(buf, sizeof buf, "%ld", (long)pid); + if (write(tasks_fd, buf, n) < n) { + die("cannot move process %ld to freezer cgroup hierarchy for snap %s", (long)pid, snap_name); + } + debug("moved process %ld to freezer cgroup hierarchy for snap %s", + (long)pid, snap_name); +} diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/cgroup-freezer-support.h snapd-2.29.3/cmd/libsnap-confine-private/cgroup-freezer-support.h --- snapd-2.28.5/cmd/libsnap-confine-private/cgroup-freezer-support.h 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/cgroup-freezer-support.h 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,26 @@ +#ifndef SC_CGROUP_FREEZER_SUPPORT_H +#define SC_CGROUP_FREEZER_SUPPORT_H + +#include +#include "error.h" + +/** + * Join the freezer cgroup for the given snap. + * + * This function adds the specified task to the freezer cgroup specific to the + * given snap. The name of the cgroup is "snap.$snap_name". + * + * Interestingly we don't need to actually freeze the processes. The group + * allows us to track processes belonging to a given snap. This makes the + * measurement "are any processes of this snap still alive" very simple. + * + * The "tasks" file belonging to the cgroup contains the set of all the + * processes that originate from the given snap. Examining that file one can + * reliably determine if the set is empty or not. + * + * For more details please review: + * https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt +**/ +void sc_cgroup_freezer_join(const char *snap_name, pid_t pid); + +#endif diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/classic.c snapd-2.29.3/cmd/libsnap-confine-private/classic.c --- snapd-2.28.5/cmd/libsnap-confine-private/classic.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/classic.c 2017-10-23 06:17:27.000000000 +0000 @@ -1,15 +1,25 @@ #include "config.h" #include "classic.h" +#include "../libsnap-confine-private/cleanup-funcs.h" +#include +#include #include +char *os_release = "/etc/os-release"; + bool is_running_on_classic_distribution() { - // NOTE: keep this list sorted please - return false - || access("/var/lib/dpkg/status", F_OK) == 0 - || access("/var/lib/pacman", F_OK) == 0 - || access("/var/lib/portage", F_OK) == 0 - || access("/var/lib/rpm", F_OK) == 0 - || access("/sbin/procd", F_OK) == 0; + FILE *f SC_CLEANUP(sc_cleanup_file) = fopen(os_release, "r"); + if (f == NULL) { + return true; + } + + char buf[255] = { 0 }; + while (fgets(buf, sizeof buf, f) != NULL) { + if (strcmp(buf, "ID=ubuntu-core\n") == 0) { + return false; + } + } + return true; } diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/classic-test.c snapd-2.29.3/cmd/libsnap-confine-private/classic-test.c --- snapd-2.28.5/cmd/libsnap-confine-private/classic-test.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/classic-test.c 2017-10-23 06:17:27.000000000 +0000 @@ -20,4 +20,52 @@ #include -// TODO: write some tests +const char *os_release_classic = "" + "NAME=\"Ubuntu\"\n" + "VERSION=\"17.04 (Zesty Zapus)\"\n" "ID=ubuntu\n" "ID_LIKE=debian\n"; + +static void test_is_on_classic() +{ + g_file_set_contents("os-release.classic", os_release_classic, + strlen(os_release_classic), NULL); + os_release = "os-release.classic"; + g_assert_true(is_running_on_classic_distribution()); + unlink("os-release.classic"); +} + +const char *os_release_core = "" + "NAME=\"Ubuntu Core\"\n" "VERSION=\"16\"\n" "ID=ubuntu-core\n"; + +static void test_is_on_core() +{ + g_file_set_contents("os-release.core", os_release_core, + strlen(os_release_core), NULL); + os_release = "os-release.core"; + g_assert_false(is_running_on_classic_distribution()); + unlink("os-release.core"); +} + +const char *os_release_classic_with_long_line = "" + "NAME=\"Ubuntu\"\n" + "VERSION=\"17.04 (Zesty Zapus)\"\n" + "ID=ubuntu\n" + "ID_LIKE=debian\n" + "LONG=line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line.line."; + +static void test_is_on_classic_with_long_line() +{ + g_file_set_contents("os-release.classic-with-long-line", + os_release_classic, strlen(os_release_classic), + NULL); + os_release = "os-release.classic-with-long-line"; + g_assert_true(is_running_on_classic_distribution()); + unlink("os-release.classic-with-long-line"); +} + +static void __attribute__ ((constructor)) init() +{ + g_test_add_func("/classic/on-classic", test_is_on_classic); + g_test_add_func("/classic/on-classic-with-long-line", + test_is_on_classic_with_long_line); + g_test_add_func("/classic/on-core", test_is_on_core); +} diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/cleanup-funcs.h snapd-2.29.3/cmd/libsnap-confine-private/cleanup-funcs.h --- snapd-2.28.5/cmd/libsnap-confine-private/cleanup-funcs.h 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/cleanup-funcs.h 2017-10-23 06:17:27.000000000 +0000 @@ -27,6 +27,10 @@ #include #include +// SC_CLEANUP will run the given cleanup function when the variable next +// to it goes out of scope. +#define SC_CLEANUP(n) __attribute__((cleanup(n))) + /** * Free a dynamically allocated string. * diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/cleanup-funcs-test.c snapd-2.29.3/cmd/libsnap-confine-private/cleanup-funcs-test.c --- snapd-2.28.5/cmd/libsnap-confine-private/cleanup-funcs-test.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/cleanup-funcs-test.c 2017-10-23 06:17:27.000000000 +0000 @@ -28,7 +28,7 @@ called = 1; } { - int test __attribute__ ((cleanup(fn))); + int test SC_CLEANUP(fn); test = 0; test++; } diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/locking.c snapd-2.29.3/cmd/libsnap-confine-private/locking.c --- snapd-2.28.5/cmd/libsnap-confine-private/locking.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/locking.c 2017-10-27 12:23:38.000000000 +0000 @@ -60,7 +60,7 @@ if (sigaction(SIGALRM, &act, NULL) < 0) { die("cannot install signal handler for SIGALRM"); } - alarm(3); + alarm(6); debug("sanity timeout initialized and set for three seconds"); } @@ -92,14 +92,14 @@ die("cannot create lock directory %s", sc_lock_dir); } debug("opening lock directory %s", sc_lock_dir); - int dir_fd __attribute__ ((cleanup(sc_cleanup_close))) = -1; + int dir_fd SC_CLEANUP(sc_cleanup_close) = -1; dir_fd = open(sc_lock_dir, O_DIRECTORY | O_PATH | O_CLOEXEC | O_NOFOLLOW); if (dir_fd < 0) { die("cannot open lock directory"); } // Construct the name of the lock file. - char lock_fname[PATH_MAX]; + char lock_fname[PATH_MAX] = { 0 }; sc_must_snprintf(lock_fname, sizeof lock_fname, "%s/%s.lock", sc_lock_dir, scope ? : ""); diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/locking-test.c snapd-2.29.3/cmd/libsnap-confine-private/locking-test.c --- snapd-2.28.5/cmd/libsnap-confine-private/locking-test.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/locking-test.c 2017-10-27 12:23:38.000000000 +0000 @@ -65,12 +65,12 @@ const char *lock_dir = sc_test_use_fake_lock_dir(); int fd = sc_lock("foo"); // Construct the name of the lock file - char *lock_file __attribute__ ((cleanup(sc_cleanup_string))) = NULL; + char *lock_file SC_CLEANUP(sc_cleanup_string) = NULL; lock_file = g_strdup_printf("%s/foo.lock", lock_dir); // Open the lock file again to obtain a separate file descriptor. // According to flock(2) locks are associated with an open file table entry // so this descriptor will be separate and can compete for the same lock. - int lock_fd __attribute__ ((cleanup(sc_cleanup_close))) = -1; + int lock_fd SC_CLEANUP(sc_cleanup_close) = -1; lock_fd = open(lock_file, O_RDWR | O_CLOEXEC | O_NOFOLLOW); g_assert_cmpint(lock_fd, !=, -1); // The non-blocking lock operation should fail with EWOULDBLOCK as the lock @@ -91,7 +91,7 @@ if (g_test_subprocess()) { sc_enable_sanity_timeout(); debug("waiting..."); - usleep(4 * G_USEC_PER_SEC); + usleep(7 * G_USEC_PER_SEC); debug("woke up"); sc_disable_sanity_timeout(); return; diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/mountinfo.c snapd-2.29.3/cmd/libsnap-confine-private/mountinfo.c --- snapd-2.28.5/cmd/libsnap-confine-private/mountinfo.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/mountinfo.c 2017-10-23 06:17:27.000000000 +0000 @@ -78,13 +78,13 @@ if (fname == NULL) { fname = "/proc/self/mountinfo"; } - FILE *f __attribute__ ((cleanup(sc_cleanup_file))) = NULL; + FILE *f SC_CLEANUP(sc_cleanup_file) = NULL; f = fopen(fname, "rt"); if (f == NULL) { free(info); return NULL; } - char *line __attribute__ ((cleanup(sc_cleanup_string))) = NULL; + char *line SC_CLEANUP(sc_cleanup_string) = NULL; size_t line_size = 0; struct sc_mountinfo_entry *entry, *last = NULL; for (;;) { diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/mount-opt.c snapd-2.29.3/cmd/libsnap-confine-private/mount-opt.c --- snapd-2.28.5/cmd/libsnap-confine-private/mount-opt.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/mount-opt.c 2017-10-23 06:17:27.000000000 +0000 @@ -109,7 +109,7 @@ #undef F // Render any flags that are unaccounted for. if (flags) { - char of[128]; + char of[128] = { 0 }; sc_must_snprintf(of, sizeof of, "%#lx", flags); sc_string_append(buf, buf_size, of); } @@ -197,7 +197,7 @@ } // If regular option syntax exists then use it. if (mountflags & ~used_special_flags) { - char opts_buf[1000]; + char opts_buf[1000] = { 0 }; sc_mount_opt2str(opts_buf, sizeof opts_buf, mountflags & ~used_special_flags); sc_string_append(buf, buf_size, " -o "); @@ -249,7 +249,7 @@ const char *fs_type, unsigned long mountflags, const void *data) { - char buf[10000]; + char buf[10000] = { 0 }; const char *mount_cmd = NULL; void ensure_mount_cmd() { @@ -288,7 +288,7 @@ void sc_do_umount(const char *target, int flags) { - char buf[10000]; + char buf[10000] = { 0 }; const char *umount_cmd = NULL; void ensure_umount_cmd() { diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/mount-opt-test.c snapd-2.29.3/cmd/libsnap-confine-private/mount-opt-test.c --- snapd-2.28.5/cmd/libsnap-confine-private/mount-opt-test.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/mount-opt-test.c 2017-10-23 06:17:27.000000000 +0000 @@ -25,7 +25,7 @@ static void test_sc_mount_opt2str() { - char buf[1000]; + char buf[1000] = { 0 }; g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, 0), ==, ""); g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_RDONLY), ==, "ro"); g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_NOSUID), ==, @@ -93,7 +93,7 @@ static void test_sc_mount_cmd() { - char cmd[10000]; + char cmd[10000] = { 0 }; // Typical mount sc_mount_cmd(cmd, sizeof cmd, "/dev/sda3", "/mnt", "ext4", MS_RDONLY, @@ -146,8 +146,8 @@ g_assert_cmpstr(cmd, ==, "mount --move /from /to"); // Monster (invalid but let's format it) - char from[PATH_MAX]; - char to[PATH_MAX]; + char from[PATH_MAX] = { 0 }; + char to[PATH_MAX] = { 0 }; for (int i = 1; i < PATH_MAX - 1; ++i) { from[i] = 'a'; to[i] = 'b'; @@ -176,7 +176,7 @@ static void test_sc_umount_cmd() { - char cmd[1000]; + char cmd[1000] = { 0 }; // Typical umount sc_umount_cmd(cmd, sizeof cmd, "/mnt/foo", 0); diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/snap.c snapd-2.29.3/cmd/libsnap-confine-private/snap.c --- snapd-2.28.5/cmd/libsnap-confine-private/snap.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/snap.c 2017-10-23 06:17:27.000000000 +0000 @@ -99,6 +99,8 @@ void sc_snap_name_validate(const char *snap_name, struct sc_error **errorp) { + // NOTE: This function should be synchronized with the two other + // implementations: validate_snap_name and snap.ValidateName. struct sc_error *err = NULL; // Ensure that name is not NULL diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/string-utils-test.c snapd-2.29.3/cmd/libsnap-confine-private/string-utils-test.c --- snapd-2.28.5/cmd/libsnap-confine-private/string-utils-test.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/string-utils-test.c 2017-10-23 06:17:27.000000000 +0000 @@ -55,7 +55,7 @@ static void test_sc_must_snprintf() { - char buf[5]; + char buf[5] = { 0 }; sc_must_snprintf(buf, sizeof buf, "1234"); g_assert_cmpstr(buf, ==, "1234"); } @@ -140,7 +140,7 @@ static void test_sc_string_append__overflow() { if (g_test_subprocess()) { - char buf[4] = { 0, }; + char buf[4] = { 0 }; // Try to append a string that's one character too long. sc_string_append(buf, sizeof buf, "1234"); @@ -829,5 +829,8 @@ test_sc_string_append_char_pair__normal); g_test_add_func("/string-utils/sc_string_quote__NULL_buf", test_sc_string_quote_NULL_str); + g_test_add_func + ("/string-utils/sc_string_append_char_pair__uninitialized_buf", + test_sc_string_append_char_pair__uninitialized_buf); g_test_add_func("/string-utils/sc_string_quote", test_sc_string_quote); } diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/utils.c snapd-2.29.3/cmd/libsnap-confine-private/utils.c --- snapd-2.28.5/cmd/libsnap-confine-private/utils.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/utils.c 2017-10-23 06:17:27.000000000 +0000 @@ -66,20 +66,22 @@ }; /** - * Convert string to a boolean value. + * Convert string to a boolean value, with a default. * * The return value is 0 in case of success or -1 when the string cannot be * converted correctly. In such case errno is set to indicate the problem and * the value is not written back to the caller-supplied pointer. + * + * If the text cannot be recognized, the default value is used. **/ -static int str2bool(const char *text, bool * value) +static int parse_bool(const char *text, bool * value, bool default_value) { if (value == NULL) { errno = EFAULT; return -1; } if (text == NULL) { - *value = false; + *value = default_value; return 0; } for (int i = 0; i < sizeof sc_bool_names / sizeof *sc_bool_names; ++i) { @@ -95,15 +97,16 @@ /** * Get an environment variable and convert it to a boolean. * - * Supported values are those of str2bool(), namely "yes", "no" as well as "1" + * Supported values are those of parse_bool(), namely "yes", "no" as well as "1" * and "0". All other values are treated as false and a diagnostic message is - * printed to stderr. + * printed to stderr. If the environment variable is unset, set value to the + * default_value as if the environment variable was set to default_value. **/ -static bool getenv_bool(const char *name) +static bool getenv_bool(const char *name, bool default_value) { const char *str_value = getenv(name); - bool value; - if (str2bool(str_value, &value) < 0) { + bool value = default_value; + if (parse_bool(str_value, &value, default_value) < 0) { if (errno == EINVAL) { fprintf(stderr, "WARNING: unrecognized value of environment variable %s (expected yes/no or 1/0)\n", @@ -118,7 +121,12 @@ bool sc_is_debug_enabled() { - return getenv_bool("SNAP_CONFINE_DEBUG"); + return getenv_bool("SNAP_CONFINE_DEBUG", false); +} + +bool sc_is_reexec_enabled() +{ + return getenv_bool("SNAP_REEXEC", true); } void debug(const char *msg, ...) @@ -155,7 +163,7 @@ } // We're going to use strtok_r, which needs to modify the path, so we'll // make a copy of it. - char *path_copy __attribute__ ((cleanup(sc_cleanup_string))) = NULL; + char *path_copy SC_CLEANUP(sc_cleanup_string) = NULL; path_copy = strdup(path); if (path_copy == NULL) { return -1; @@ -170,7 +178,7 @@ // of mkdir calls, to avoid following symlinks and placing the user data // directory somewhere we never intended for it to go. The first step is to // get an initial file descriptor. - int fd __attribute__ ((cleanup(sc_cleanup_close))) = AT_FDCWD; + int fd SC_CLEANUP(sc_cleanup_close) = AT_FDCWD; if (path_copy[0] == '/') { fd = open("/", open_flags); if (fd < 0) { diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/utils.h snapd-2.29.3/cmd/libsnap-confine-private/utils.h --- snapd-2.28.5/cmd/libsnap-confine-private/utils.h 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/utils.h 2017-10-23 06:17:27.000000000 +0000 @@ -37,6 +37,11 @@ **/ bool sc_is_debug_enabled(); +/** + * Return true if re-execution is enabled. + **/ +bool sc_is_reexec_enabled(); + void write_string_to_file(const char *filepath, const char *buf); /** diff -Nru snapd-2.28.5/cmd/libsnap-confine-private/utils-test.c snapd-2.29.3/cmd/libsnap-confine-private/utils-test.c --- snapd-2.28.5/cmd/libsnap-confine-private/utils-test.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/libsnap-confine-private/utils-test.c 2017-10-23 06:17:27.000000000 +0000 @@ -20,40 +20,53 @@ #include -static void test_str2bool() +static void test_parse_bool() { int err; bool value; - err = str2bool("yes", &value); + value = false; + err = parse_bool("yes", &value, false); g_assert_cmpint(err, ==, 0); g_assert_true(value); - err = str2bool("1", &value); + value = false; + err = parse_bool("1", &value, false); g_assert_cmpint(err, ==, 0); g_assert_true(value); - err = str2bool("no", &value); + value = true; + err = parse_bool("no", &value, false); g_assert_cmpint(err, ==, 0); g_assert_false(value); - err = str2bool("0", &value); + value = true; + err = parse_bool("0", &value, false); g_assert_cmpint(err, ==, 0); g_assert_false(value); - err = str2bool("", &value); + value = true; + err = parse_bool("", &value, false); g_assert_cmpint(err, ==, 0); g_assert_false(value); - err = str2bool(NULL, &value); + value = true; + err = parse_bool(NULL, &value, false); g_assert_cmpint(err, ==, 0); g_assert_false(value); - err = str2bool("flower", &value); + value = false; + err = parse_bool(NULL, &value, true); + g_assert_cmpint(err, ==, 0); + g_assert_true(value); + + value = true; + err = parse_bool("flower", &value, false); g_assert_cmpint(err, ==, -1); g_assert_cmpint(errno, ==, EINVAL); + g_assert_true(value); - err = str2bool("yes", NULL); + err = parse_bool("yes", NULL, false); g_assert_cmpint(err, ==, -1); g_assert_cmpint(errno, ==, EFAULT); } @@ -164,7 +177,7 @@ static void __attribute__ ((constructor)) init() { - g_test_add_func("/utils/str2bool", test_str2bool); + g_test_add_func("/utils/parse_bool", test_parse_bool); g_test_add_func("/utils/die", test_die); g_test_add_func("/utils/die_with_errno", test_die_with_errno); g_test_add_func("/utils/sc_nonfatal_mkpath/relative", diff -Nru snapd-2.28.5/cmd/Makefile.am snapd-2.29.3/cmd/Makefile.am --- snapd-2.28.5/cmd/Makefile.am 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/Makefile.am 2017-10-23 06:17:27.000000000 +0000 @@ -43,15 +43,19 @@ # The hack target helps devlopers work on snap-confine on their live system by # installing a fresh copy of snap confine and the appropriate apparmor profile. .PHONY: hack -hack: snap-confine/snap-confine snap-confine/snap-confine.apparmor snap-update-ns/snap-update-ns +hack: snap-confine/snap-confine snap-confine/snap-confine.apparmor snap-update-ns/snap-update-ns snap-seccomp/snap-seccomp sudo install -D -m 4755 snap-confine/snap-confine $(DESTDIR)$(libexecdir)/snap-confine - sudo install -m 644 snap-confine/snap-confine.apparmor $(DESTDIR)/etc/apparmor.d/$(patsubst .%,%,$(subst /,.,$(libexecdir))).snap-confine + sudo install -m 644 snap-confine/snap-confine.apparmor $(DESTDIR)/etc/apparmor.d/$(patsubst .%,%,$(subst /,.,$(libexecdir))).snap-confine.real + sudo install -d -m 755 $(DESTDIR)/var/lib/snapd/apparmor/snap-confine.d/ sudo apparmor_parser -r snap-confine/snap-confine.apparmor sudo install -m 755 snap-update-ns/snap-update-ns $(DESTDIR)$(libexecdir)/snap-update-ns + sudo install -m 755 snap-seccomp/snap-seccomp $(DESTDIR)$(libexecdir)/snap-seccomp # for the hack target also: snap-update-ns/snap-update-ns: snap-update-ns/*.go snap-update-ns/*.[ch] cd snap-update-ns && GOPATH=$(or $(GOPATH),$(realpath $(srcdir)/../../../../..)) go build -i -v +snap-seccomp/snap-seccomp: snap-seccomp/*.go + cd snap-seccomp && GOPATH=$(or $(GOPATH),$(realpath $(srcdir)/../../../../..)) go build -i -v ## ## libsnap-confine-private.a @@ -60,6 +64,8 @@ noinst_LIBRARIES += libsnap-confine-private.a libsnap_confine_private_a_SOURCES = \ + libsnap-confine-private/cgroup-freezer-support.c \ + libsnap-confine-private/cgroup-freezer-support.h \ libsnap-confine-private/classic.c \ libsnap-confine-private/classic.h \ libsnap-confine-private/cleanup-funcs.c \ @@ -157,8 +163,8 @@ libexec_PROGRAMS += snap-confine/snap-confine if HAVE_RST2MAN -dist_man_MANS += snap-confine/snap-confine.5 -CLEANFILES += snap-confine/snap-confine.5 +dist_man_MANS += snap-confine/snap-confine.1 +CLEANFILES += snap-confine/snap-confine.1 endif EXTRA_DIST += snap-confine/snap-confine.rst EXTRA_DIST += snap-confine/snap-confine.apparmor.in @@ -277,7 +283,7 @@ endif # WITH_UNIT_TESTS if HAVE_RST2MAN -snap-confine/%.5: snap-confine/%.rst +snap-confine/%.1: snap-confine/%.rst mkdir -p snap-confine $(HAVE_RST2MAN) $^ > $@ endif @@ -295,6 +301,7 @@ install -d -m 755 $(DESTDIR)/etc/apparmor.d/ install -m 644 snap-confine/snap-confine.apparmor $(DESTDIR)/etc/apparmor.d/$(patsubst .%,%,$(subst /,.,$(libexecdir))).snap-confine endif + install -d -m 755 $(DESTDIR)/var/lib/snapd/apparmor/snap-confine.d/ # NOTE: The 'void' directory *has to* be chmod 000 install-data-local:: diff -Nru snapd-2.28.5/cmd/snap/cmd_ack.go snapd-2.29.3/cmd/snap/cmd_ack.go --- snapd-2.28.5/cmd/snap/cmd_ack.go 2016-12-08 15:14:07.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_ack.go 2017-10-27 12:23:38.000000000 +0000 @@ -50,7 +50,9 @@ addCommand("ack", shortAckHelp, longAckHelp, func() flags.Commander { return &cmdAck{} }, nil, []argDesc{{ + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Assertion file"), }}) } diff -Nru snapd-2.28.5/cmd/snap/cmd_alias.go snapd-2.29.3/cmd/snap/cmd_alias.go --- snapd-2.28.5/cmd/snap/cmd_alias.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_alias.go 2017-10-27 12:23:38.000000000 +0000 @@ -33,8 +33,8 @@ type cmdAlias struct { Positionals struct { - SnapApp string `required:"yes"` - Alias string `required:"yes"` + SnapApp appName `required:"yes"` + Alias string `required:"yes"` } `positional-args:"true"` } @@ -52,6 +52,7 @@ return &cmdAlias{} }, nil, []argDesc{ {name: ""}, + // TRANSLATORS: This needs to be wrapped in <>s. {name: i18n.G("")}, }) } @@ -61,7 +62,7 @@ return ErrExtraArgs } - snapName, appName := snap.SplitSnapApp(x.Positionals.SnapApp) + snapName, appName := snap.SplitSnapApp(string(x.Positionals.SnapApp)) alias := x.Positionals.Alias cli := Client() diff -Nru snapd-2.28.5/cmd/snap/cmd_auto_import_test.go snapd-2.29.3/cmd/snap/cmd_auto_import_test.go --- snapd-2.28.5/cmd/snap/cmd_auto_import_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_auto_import_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -85,9 +85,8 @@ restore = snap.MockMountInfoPath(makeMockMountInfo(c, content)) defer restore() - l, err := logger.New(s.stderr, 0) - c.Assert(err, IsNil) - logger.SetLogger(l) + logbuf, restore := logger.MockLogger() + defer restore() rest, err := snap.Parser().ParseArgs([]string{"auto-import"}) c.Assert(err, IsNil) @@ -96,7 +95,7 @@ // matches because we may get a: // "WARNING: cannot create syslog logger\n" // in the output - c.Check(s.Stderr(), Matches, fmt.Sprintf("(?ms).*imported %s\n", fakeAssertsFn)) + c.Check(logbuf.String(), Matches, fmt.Sprintf("(?ms).*imported %s\n", fakeAssertsFn)) c.Check(n, Equals, total) } @@ -187,9 +186,8 @@ restore := release.MockOnClassic(false) defer restore() - l, err := logger.New(s.stderr, 0) - c.Assert(err, IsNil) - logger.SetLogger(l) + logbuf, restore := logger.MockLogger() + defer restore() fakeAssertData := []byte("good-assertion") @@ -197,7 +195,7 @@ snap.ClientConfig.BaseURL = "can-not-connect-to-this-url" fakeAssertsFn := filepath.Join(c.MkDir(), "auto-import.assert") - err = ioutil.WriteFile(fakeAssertsFn, fakeAssertData, 0644) + err := ioutil.WriteFile(fakeAssertsFn, fakeAssertData, 0644) c.Assert(err, IsNil) mockMountInfoFmt := ` @@ -213,7 +211,7 @@ // matches because we may get a: // "WARNING: cannot create syslog logger\n" // in the output - c.Check(s.Stderr(), Matches, "(?ms).*queuing for later.*\n") + c.Check(logbuf.String(), Matches, "(?ms).*queuing for later.*\n") files, err := ioutil.ReadDir(dirs.SnapAssertsSpoolDir) c.Assert(err, IsNil) @@ -260,9 +258,8 @@ err = ioutil.WriteFile(fakeAssertsFn, fakeAssertData, 0644) c.Assert(err, IsNil) - l, err := logger.New(s.stderr, 0) - c.Assert(err, IsNil) - logger.SetLogger(l) + logbuf, restore := logger.MockLogger() + defer restore() rest, err := snap.Parser().ParseArgs([]string{"auto-import"}) c.Assert(err, IsNil) @@ -271,7 +268,7 @@ // matches because we may get a: // "WARNING: cannot create syslog logger\n" // in the output - c.Check(s.Stderr(), Matches, fmt.Sprintf("(?ms).*imported %s\n", fakeAssertsFn)) + c.Check(logbuf.String(), Matches, fmt.Sprintf("(?ms).*imported %s\n", fakeAssertsFn)) c.Check(n, Equals, total) c.Check(osutil.FileExists(fakeAssertsFn), Equals, false) @@ -281,9 +278,8 @@ restore := release.MockOnClassic(false) defer restore() - l, err := logger.New(s.stderr, 0) - c.Assert(err, IsNil) - logger.SetLogger(l) + _, restoreLogger := logger.MockLogger() + defer restoreLogger() // fake data is bigger than the default assertion limit fakeAssertData := make([]byte, 641*1024) @@ -292,7 +288,7 @@ snap.ClientConfig.BaseURL = "can-not-connect-to-this-url" fakeAssertsFn := filepath.Join(c.MkDir(), "auto-import.assert") - err = ioutil.WriteFile(fakeAssertsFn, fakeAssertData, 0644) + err := ioutil.WriteFile(fakeAssertsFn, fakeAssertData, 0644) c.Assert(err, IsNil) mockMountInfoFmt := ` diff -Nru snapd-2.28.5/cmd/snap/cmd_buy.go snapd-2.29.3/cmd/snap/cmd_buy.go --- snapd-2.28.5/cmd/snap/cmd_buy.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_buy.go 2017-10-27 12:23:38.000000000 +0000 @@ -46,6 +46,7 @@ return &cmdBuy{} }, map[string]string{}, []argDesc{{ name: "", + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Snap name"), }}) } diff -Nru snapd-2.28.5/cmd/snap/cmd_connect.go snapd-2.29.3/cmd/snap/cmd_connect.go --- snapd-2.28.5/cmd/snap/cmd_connect.go 2017-08-18 13:48:10.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_connect.go 2017-10-27 12:23:38.000000000 +0000 @@ -57,7 +57,9 @@ addCommand("connect", shortConnectHelp, longConnectHelp, func() flags.Commander { return &cmdConnect{} }, nil, []argDesc{ + // TRANSLATORS: This needs to be wrapped in <>s. {name: i18n.G(":")}, + // TRANSLATORS: This needs to be wrapped in <>s. {name: i18n.G(":")}, }) } diff -Nru snapd-2.28.5/cmd/snap/cmd_create_key.go snapd-2.29.3/cmd/snap/cmd_create_key.go --- snapd-2.28.5/cmd/snap/cmd_create_key.go 2016-09-15 18:55:10.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_create_key.go 2017-10-27 12:23:38.000000000 +0000 @@ -43,7 +43,9 @@ func() flags.Commander { return &cmdCreateKey{} }, nil, []argDesc{{ + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Name of key to create; defaults to 'default'"), }}) cmd.hidden = true diff -Nru snapd-2.28.5/cmd/snap/cmd_create_user.go snapd-2.29.3/cmd/snap/cmd_create_user.go --- snapd-2.28.5/cmd/snap/cmd_create_user.go 2016-10-27 13:22:15.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_create_user.go 2017-10-27 12:23:38.000000000 +0000 @@ -56,9 +56,9 @@ "known": i18n.G("Use known assertions for user creation"), "force-managed": i18n.G("Force adding the user, even if the device is already managed"), }, []argDesc{{ - // TRANSLATORS: noun + // TRANSLATORS: This is a noun, and it needs to be wrapped in <>s. name: i18n.G(""), - // TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses + // TRANSLATORS: This should probably not start with a lowercase letter. Also, note users on login.ubuntu.com can have multiple email addresses. desc: i18n.G("An email of a user on login.ubuntu.com"), }}) cmd.hidden = true diff -Nru snapd-2.28.5/cmd/snap/cmd_delete_key.go snapd-2.29.3/cmd/snap/cmd_delete_key.go --- snapd-2.28.5/cmd/snap/cmd_delete_key.go 2016-12-08 15:14:07.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_delete_key.go 2017-10-27 12:23:38.000000000 +0000 @@ -39,7 +39,9 @@ func() flags.Commander { return &cmdDeleteKey{} }, nil, []argDesc{{ + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Name of key to delete"), }}) cmd.hidden = true diff -Nru snapd-2.28.5/cmd/snap/cmd_disconnect.go snapd-2.29.3/cmd/snap/cmd_disconnect.go --- snapd-2.28.5/cmd/snap/cmd_disconnect.go 2017-08-18 13:48:10.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_disconnect.go 2017-10-27 12:23:38.000000000 +0000 @@ -53,7 +53,9 @@ addCommand("disconnect", shortDisconnectHelp, longDisconnectHelp, func() flags.Commander { return &cmdDisconnect{} }, nil, []argDesc{ + // TRANSLATORS: This needs to be wrapped in <>s. {name: i18n.G(":")}, + // TRANSLATORS: This needs to be wrapped in <>s. {name: i18n.G(":")}, }) } diff -Nru snapd-2.28.5/cmd/snap/cmd_download.go snapd-2.29.3/cmd/snap/cmd_download.go --- snapd-2.28.5/cmd/snap/cmd_download.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_download.go 2017-10-27 12:23:38.000000000 +0000 @@ -56,6 +56,7 @@ "revision": i18n.G("Download the given revision of a snap, to which you must have developer access"), }), []argDesc{{ name: "", + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Snap name"), }}) } diff -Nru snapd-2.28.5/cmd/snap/cmd_export_key.go snapd-2.29.3/cmd/snap/cmd_export_key.go --- snapd-2.28.5/cmd/snap/cmd_export_key.go 2016-12-08 15:14:07.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_export_key.go 2017-10-27 12:23:38.000000000 +0000 @@ -45,7 +45,9 @@ }, map[string]string{ "account": i18n.G("Format public key material as a request for an account-key for this account-id"), }, []argDesc{{ + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Name of key to export"), }}) cmd.hidden = true diff -Nru snapd-2.28.5/cmd/snap/cmd_find.go snapd-2.29.3/cmd/snap/cmd_find.go --- snapd-2.28.5/cmd/snap/cmd_find.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_find.go 2017-10-27 12:23:38.000000000 +0000 @@ -27,6 +27,7 @@ "github.com/jessevdk/go-flags" "github.com/snapcore/snapd/client" + "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/i18n" ) @@ -68,6 +69,10 @@ type SectionName string func (s SectionName) Complete(match string) []flags.Completion { + if ret, err := completeFromSortedFile(dirs.SnapSectionsFile, match); err == nil { + return ret + } + cli := Client() sections, err := cli.Sections() if err != nil { @@ -96,7 +101,10 @@ }, map[string]string{ "private": i18n.G("Search private snaps"), "section": i18n.G("Restrict the search to a given section"), - }, []argDesc{{name: i18n.G("")}}).alias = "search" + }, []argDesc{{ + // TRANSLATORS: This needs to be wrapped in <>s. + name: i18n.G(""), + }}).alias = "search" } func (x *cmdFind) Execute(args []string) error { diff -Nru snapd-2.28.5/cmd/snap/cmd_get.go snapd-2.29.3/cmd/snap/cmd_get.go --- snapd-2.28.5/cmd/snap/cmd_get.go 2016-12-08 15:14:07.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_get.go 2017-10-27 12:23:38.000000000 +0000 @@ -22,11 +22,14 @@ import ( "encoding/json" "fmt" + "os" + "sort" "strings" "github.com/jessevdk/go-flags" "github.com/snapcore/snapd/i18n" + "golang.org/x/crypto/ssh/terminal" ) var shortGetHelp = i18n.G("Prints configuration options") @@ -52,58 +55,163 @@ type cmdGet struct { Positional struct { - Snap installedSnapName + Snap installedSnapName `required:"yes"` Keys []string - } `positional-args:"yes" required:"yes"` + } `positional-args:"yes"` Typed bool `short:"t"` Document bool `short:"d"` + List bool `short:"l"` } func init() { addCommand("get", shortGetHelp, longGetHelp, func() flags.Commander { return &cmdGet{} }, map[string]string{ "d": i18n.G("Always return document, even with single key"), + "l": i18n.G("Always return list, even with single key"), "t": i18n.G("Strict typing with nulls and quoted strings"), }, []argDesc{ { name: "", + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("The snap whose conf is being requested"), }, { + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Key of interest within the configuration"), }, }) } -func (x *cmdGet) Execute(args []string) error { - if len(args) > 0 { - // TRANSLATORS: the %s is the list of extra arguments - return fmt.Errorf(i18n.G("too many arguments: %s"), strings.Join(args, " ")) +type ConfigValue struct { + Path string + Value interface{} +} + +type byConfigPath []ConfigValue + +func (s byConfigPath) Len() int { return len(s) } +func (s byConfigPath) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s byConfigPath) Less(i, j int) bool { + other := s[j].Path + for k, c := range s[i].Path { + if len(other) <= k { + return false + } + + switch { + case c == rune(other[k]): + continue + case c == '.': + return true + case other[k] == '.' || c > rune(other[k]): + return false + } + return true } + return true +} - if x.Document && x.Typed { - return fmt.Errorf("cannot use -d and -t together") +func sortByPath(config []ConfigValue) { + sort.Sort(byConfigPath(config)) +} + +func flattenConfig(cfg map[string]interface{}, root bool) (values []ConfigValue) { + const docstr = "{...}" + for k, v := range cfg { + if input, ok := v.(map[string]interface{}); ok { + if root { + values = append(values, ConfigValue{k, docstr}) + } else { + for kk, vv := range input { + p := k + "." + kk + if _, ok := vv.(map[string]interface{}); ok { + values = append(values, ConfigValue{p, docstr}) + } else { + values = append(values, ConfigValue{p, vv}) + } + } + } + } else { + values = append(values, ConfigValue{k, v}) + } } + sortByPath(values) + return values +} - snapName := string(x.Positional.Snap) - confKeys := x.Positional.Keys +func rootRequested(confKeys []string) bool { + return len(confKeys) == 0 +} - cli := Client() - conf, err := cli.Conf(snapName, confKeys) +// outputJson will be used when the user requested "document" output via +// the "-d" commandline switch. +func (c *cmdGet) outputJson(conf interface{}) error { + bytes, err := json.MarshalIndent(conf, "", "\t") if err != nil { return err } + fmt.Fprintln(Stdout, string(bytes)) + return nil +} + +// outputList will be used when the user requested list output via the +// "-l" commandline switch. +func (x *cmdGet) outputList(conf map[string]interface{}) error { + if rootRequested(x.Positional.Keys) && len(conf) == 0 { + return fmt.Errorf("snap %q has no configuration", x.Positional.Snap) + } + + w := tabWriter() + defer w.Flush() + + fmt.Fprintf(w, "Key\tValue\n") + values := flattenConfig(conf, rootRequested(x.Positional.Keys)) + for _, v := range values { + fmt.Fprintf(w, "%s\t%v\n", v.Path, v.Value) + } + return nil +} + +var isTerminal = func() bool { + return terminal.IsTerminal(int(os.Stdin.Fd())) +} + +// outputDefault will be used when no commandline switch to override the +// output where used. The output follows the following rules: +// - a single key with a string value is printed directly +// - multiple keys are printed as a list to the terminal (if there is one) +// or as json if there is no terminal +// - the option "typed" is honored +func (x *cmdGet) outputDefault(conf map[string]interface{}, snapName string, confKeys []string) error { + if rootRequested(confKeys) && len(conf) == 0 { + return fmt.Errorf("snap %q has no configuration", snapName) + } + var confToPrint interface{} = conf - if !x.Document && len(confKeys) == 1 { - confToPrint = conf[confKeys[0]] + + if len(confKeys) == 1 { + // if single key was requested, then just output the + // value unless it's a map, in which case it will be + // printed as a list below. + if _, ok := conf[confKeys[0]].(map[string]interface{}); !ok { + confToPrint = conf[confKeys[0]] + } } - if x.Typed && confToPrint == nil { - fmt.Fprintln(Stdout, "null") - return nil + // conf looks like a map + if cfg, ok := confToPrint.(map[string]interface{}); ok { + if isTerminal() { + return x.outputList(cfg) + } + + // TODO: remove this conditional and the warning below + // after a transition period. + fmt.Fprintf(Stderr, i18n.G(`WARNING: The output of "snap get" will become a list with columns - use -d or -l to force the output format.\n`)) + return x.outputJson(confToPrint) } if s, ok := confToPrint.(string); ok && !x.Typed { @@ -111,14 +219,44 @@ return nil } - var bytes []byte - if confToPrint != nil { - bytes, err = json.MarshalIndent(confToPrint, "", "\t") - if err != nil { - return err - } + if confToPrint != nil || x.Typed { + return x.outputJson(confToPrint) } - fmt.Fprintln(Stdout, string(bytes)) + fmt.Fprintln(Stdout, "") return nil + +} + +func (x *cmdGet) Execute(args []string) error { + if len(args) > 0 { + // TRANSLATORS: the %s is the list of extra arguments + return fmt.Errorf(i18n.G("too many arguments: %s"), strings.Join(args, " ")) + } + + if x.Document && x.Typed { + return fmt.Errorf("cannot use -d and -t together") + } + + if x.Document && x.List { + return fmt.Errorf("cannot use -d and -l together") + } + + snapName := string(x.Positional.Snap) + confKeys := x.Positional.Keys + + cli := Client() + conf, err := cli.Conf(snapName, confKeys) + if err != nil { + return err + } + + switch { + case x.Document: + return x.outputJson(conf) + case x.List: + return x.outputList(conf) + default: + return x.outputDefault(conf, snapName, confKeys) + } } diff -Nru snapd-2.28.5/cmd/snap/cmd_get_test.go snapd-2.29.3/cmd/snap/cmd_get_test.go --- snapd-2.28.5/cmd/snap/cmd_get_test.go 2016-10-27 13:22:15.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_get_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -29,9 +29,12 @@ snapset "github.com/snapcore/snapd/cmd/snap" ) -var getTests = []struct { - args, stdout, error string -}{{ +type getCmdArgs struct { + args, stdout, stderr, error string + isTerminal bool +} + +var getTests = []getCmdArgs{{ args: "get snap-name --foo", error: ".*unknown flag.*foo.*", }, { @@ -56,29 +59,128 @@ args: "get -d snapname test-key1", stdout: "{\n\t\"test-key1\": \"test-value1\"\n}\n", }, { - args: "get snapname test-key1 test-key2", + args: "get -l snapname test-key1", + stdout: "Key Value\ntest-key1 test-value1\n", +}, { + args: "get snapname -l test-key1 test-key2", + stdout: "Key Value\ntest-key1 test-value1\ntest-key2 2\n", +}, { + args: "get snapname document", + stderr: `WARNING: The output of "snap get" will become a list with columns - use -d or -l to force the output format.\n`, + stdout: "{\n\t\"document\": {\n\t\t\"key1\": \"value1\",\n\t\t\"key2\": \"value2\"\n\t}\n}\n", +}, { + isTerminal: true, + args: "get snapname document", + stdout: "Key Value\ndocument.key1 value1\ndocument.key2 value2\n", +}, { + args: "get snapname -d test-key1 test-key2", stdout: "{\n\t\"test-key1\": \"test-value1\",\n\t\"test-key2\": 2\n}\n", -}} - -func (s *SnapSuite) TestSnapGetTests(c *C) { - s.mockGetConfigServer(c) +}, { + args: "get snapname -l document", + stdout: "Key Value\ndocument.key1 value1\ndocument.key2 value2\n", +}, { + args: "get -d snapname document", + stdout: "{\n\t\"document\": {\n\t\t\"key1\": \"value1\",\n\t\t\"key2\": \"value2\"\n\t}\n}\n", +}, { + args: "get -l snapname", + stdout: "Key Value\nbar 100\nfoo {...}\n", +}, { + args: "get snapname -l test-key3 test-key4", + stdout: "Key Value\ntest-key3.a 1\ntest-key3.b 2\ntest-key3-a 9\ntest-key4.a 3\ntest-key4.b 4\n", +}, { + args: "get -d snapname", + stdout: "{\n\t\"bar\": 100,\n\t\"foo\": {\n\t\t\"key1\": \"value1\",\n\t\t\"key2\": \"value2\"\n\t}\n}\n", +}, { + isTerminal: true, + args: "get snapname test-key1 test-key2", + stdout: "Key Value\ntest-key1 test-value1\ntest-key2 2\n", +}, { + isTerminal: false, + args: "get snapname test-key1 test-key2", + stdout: "{\n\t\"test-key1\": \"test-value1\",\n\t\"test-key2\": 2\n}\n", + stderr: `WARNING: The output of "snap get" will become a list with columns - use -d or -l to force the output format.\n`, +}, +} - for _, test := range getTests { +func (s *SnapSuite) runTests(cmds []getCmdArgs, c *C) { + for _, test := range cmds { s.stdout.Truncate(0) s.stderr.Truncate(0) c.Logf("Test: %s", test.args) + restore := snapset.MockIsTerminal(test.isTerminal) + defer restore() + _, err := snapset.Parser().ParseArgs(strings.Fields(test.args)) if test.error != "" { c.Check(err, ErrorMatches, test.error) } else { c.Check(err, IsNil) + c.Check(s.Stderr(), Equals, test.stderr) c.Check(s.Stdout(), Equals, test.stdout) } } } +func (s *SnapSuite) TestSnapGetTests(c *C) { + s.mockGetConfigServer(c) + s.runTests(getTests, c) +} + +var getNoConfigTests = []getCmdArgs{{ + args: "get -l snapname", + error: `snap "snapname" has no configuration`, +}, { + args: "get snapname", + error: `snap "snapname" has no configuration`, +}, { + args: "get -d snapname", + stdout: "{}\n", +}} + +func (s *SnapSuite) TestSnapGetNoConfiguration(c *C) { + s.mockGetEmptyConfigServer(c) + s.runTests(getNoConfigTests, c) +} + +func (s *SnapSuite) TestSortByPath(c *C) { + values := []snapset.ConfigValue{ + {Path: "test-key3.b"}, + {Path: "a"}, + {Path: "test-key3.a"}, + {Path: "a.b.c"}, + {Path: "test-key4.a"}, + {Path: "test-key4.b"}, + {Path: "a-b"}, + {Path: "zzz"}, + {Path: "aa"}, + {Path: "test-key3-a"}, + {Path: "a.b"}, + } + snapset.SortByPath(values) + + expected := []string{ + "a", + "a.b", + "a.b.c", + "a-b", + "aa", + "test-key3.a", + "test-key3.b", + "test-key3-a", + "test-key4.a", + "test-key4.b", + "zzz", + } + + c.Assert(values, HasLen, len(expected)) + + for i, e := range expected { + c.Assert(values[i].Path, Equals, e) + } +} + func (s *SnapSuite) mockGetConfigServer(c *C) { s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/v2/snaps/snapname/conf" { @@ -96,10 +198,29 @@ fmt.Fprintln(w, `{"type":"sync", "status-code": 200, "result": {"test-key2":2}}`) case "test-key1,test-key2": fmt.Fprintln(w, `{"type":"sync", "status-code": 200, "result": {"test-key1":"test-value1","test-key2":2}}`) + case "test-key3,test-key4": + fmt.Fprintln(w, `{"type":"sync", "status-code": 200, "result": {"test-key3":{"a":1,"b":2},"test-key3-a":9,"test-key4":{"a":3,"b":4}}}`) case "missing-key": fmt.Fprintln(w, `{"type":"sync", "status-code": 200, "result": {}}`) + case "document": + fmt.Fprintln(w, `{"type":"sync", "status-code": 200, "result": {"document":{"key1":"value1","key2":"value2"}}}`) + case "": + fmt.Fprintln(w, `{"type":"sync", "status-code": 200, "result": {"foo":{"key1":"value1","key2":"value2"},"bar":100}}`) default: c.Errorf("unexpected keys %q", query.Get("keys")) } }) } + +func (s *SnapSuite) mockGetEmptyConfigServer(c *C) { + s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/v2/snaps/snapname/conf" { + c.Errorf("unexpected path %q", r.URL.Path) + return + } + + c.Check(r.Method, Equals, "GET") + + fmt.Fprintln(w, `{"type":"sync", "status-code": 200, "result": {}}`) + }) +} diff -Nru snapd-2.28.5/cmd/snap/cmd_interface.go snapd-2.29.3/cmd/snap/cmd_interface.go --- snapd-2.28.5/cmd/snap/cmd_interface.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_interface.go 2017-10-27 12:23:38.000000000 +0000 @@ -54,7 +54,9 @@ "attrs": i18n.G("Show interface attributes"), "all": i18n.G("Include unused interfaces"), }, []argDesc{{ + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Show details of a specific interface"), }}) } diff -Nru snapd-2.28.5/cmd/snap/cmd_interfaces.go snapd-2.29.3/cmd/snap/cmd_interfaces.go --- snapd-2.28.5/cmd/snap/cmd_interfaces.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_interfaces.go 2017-10-27 12:23:38.000000000 +0000 @@ -59,7 +59,9 @@ }, map[string]string{ "i": i18n.G("Constrain listing to specific interfaces"), }, []argDesc{{ + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(":"), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Constrain listing to a specific snap or snap:name"), }}) } diff -Nru snapd-2.28.5/cmd/snap/cmd_known.go snapd-2.29.3/cmd/snap/cmd_known.go --- snapd-2.28.5/cmd/snap/cmd_known.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_known.go 2017-10-27 12:23:38.000000000 +0000 @@ -53,10 +53,14 @@ return &cmdKnown{} }, nil, []argDesc{ { + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Assertion type name"), }, { + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G("
"), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Constrain listing to those matching header=value"), }, }) @@ -76,13 +80,9 @@ if at == nil { return nil, fmt.Errorf("cannot find assertion type %q", typeName) } - primaryKeys := make([]string, len(at.PrimaryKey)) - for i, k := range at.PrimaryKey { - pk, ok := headers[k] - if !ok { - return nil, fmt.Errorf("missing primary header %q to query remote assertion", k) - } - primaryKeys[i] = pk + primaryKeys, err := asserts.PrimaryKeyFromHeaders(at, headers) + if err != nil { + return nil, fmt.Errorf("cannot query remote assertion: %v", err) } sto := storeNew(nil, authContext) diff -Nru snapd-2.28.5/cmd/snap/cmd_known_test.go snapd-2.29.3/cmd/snap/cmd_known_test.go --- snapd-2.28.5/cmd/snap/cmd_known_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_known_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -57,15 +57,15 @@ if cfg == nil { cfg = store.DefaultConfig() } - serverURL, err := url.Parse(server.URL + "/assertions/") - c.Assert(err, check.IsNil) - cfg.AssertionsURI = serverURL + serverURL, _ := url.Parse(server.URL) + cfg.AssertionsBaseURL = serverURL return store.New(cfg, auth) }) defer restorer() n := 0 server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c.Assert(r.URL.Path, check.Matches, ".*/assertions/.*") // sanity check request switch n { case 0: c.Check(r.Method, check.Equals, "GET") @@ -87,7 +87,7 @@ func (s *SnapSuite) TestKnownRemoteMissingPrimaryKey(c *check.C) { _, err := snap.Parser().ParseArgs([]string{"known", "--remote", "model", "series=16", "brand-id=canonical"}) - c.Assert(err, check.ErrorMatches, `missing primary header "model" to query remote assertion`) + c.Assert(err, check.ErrorMatches, `cannot query remote assertion: must provide primary key: model`) } func (s *SnapSuite) TestAssertTypeNameCompletion(c *check.C) { diff -Nru snapd-2.28.5/cmd/snap/cmd_login.go snapd-2.29.3/cmd/snap/cmd_login.go --- snapd-2.28.5/cmd/snap/cmd_login.go 2016-12-15 08:17:53.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_login.go 2017-10-27 12:23:38.000000000 +0000 @@ -55,8 +55,9 @@ func() flags.Commander { return &cmdLogin{} }, nil, []argDesc{{ - // TRANSLATORS: noun + // TRANSLATORS: This is a noun, and it needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("The login.ubuntu.com email to login as"), }}) } diff -Nru snapd-2.28.5/cmd/snap/cmd_pack.go snapd-2.29.3/cmd/snap/cmd_pack.go --- snapd-2.28.5/cmd/snap/cmd_pack.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_pack.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,66 @@ +// -*- 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 . + * + */ + +package main + +import ( + "fmt" + + "github.com/jessevdk/go-flags" + + "github.com/snapcore/snapd/i18n" + "github.com/snapcore/snapd/snap/pack" +) + +type packCmd struct { + Positional struct { + SnapDir string `positional-arg-name:""` + TargetDir string `positional-arg-name:""` + } `positional-args:"yes"` +} + +var shortPackHelp = i18n.G("pack the given target dir as a snap") +var longPackHelp = i18n.G(` +The pack command packs the given snap-dir as a snap.`) + +func init() { + addCommand("pack", + shortPackHelp, + longPackHelp, + func() flags.Commander { + return &packCmd{} + }, nil, nil) +} + +func (x *packCmd) Execute([]string) error { + if x.Positional.SnapDir == "" { + x.Positional.SnapDir = "." + } + if x.Positional.TargetDir == "" { + x.Positional.TargetDir = "." + } + + snapPath, err := pack.Snap(x.Positional.SnapDir, x.Positional.TargetDir) + if err != nil { + return fmt.Errorf("cannot pack %q: %v", x.Positional.SnapDir, err) + + } + fmt.Fprintf(Stdout, "built: %s\n", snapPath) + return nil +} diff -Nru snapd-2.28.5/cmd/snap/cmd_prepare_image.go snapd-2.29.3/cmd/snap/cmd_prepare_image.go --- snapd-2.28.5/cmd/snap/cmd_prepare_image.go 2017-08-18 13:48:10.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_prepare_image.go 2017-10-27 12:23:38.000000000 +0000 @@ -49,10 +49,14 @@ "channel": "The channel to use", }, []argDesc{ { + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("The model assertion name"), }, { + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("The output directory"), }, }) diff -Nru snapd-2.28.5/cmd/snap/cmd_repair_repairs.go snapd-2.29.3/cmd/snap/cmd_repair_repairs.go --- snapd-2.28.5/cmd/snap/cmd_repair_repairs.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_repair_repairs.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,93 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + + "github.com/jessevdk/go-flags" + + "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/i18n" + "github.com/snapcore/snapd/release" +) + +func runSnapRepair(cmdStr string, args []string) error { + // do not even try to run snap-repair on classic, some distros + // may not even package it + if release.OnClassic { + return fmt.Errorf(i18n.G("repairs are not available on a classic system")) + } + + snapRepairPath := filepath.Join(dirs.GlobalRootDir, dirs.CoreLibExecDir, "snap-repair") + args = append([]string{cmdStr}, args...) + cmd := exec.Command(snapRepairPath, args...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + +type cmdShowRepair struct { + Positional struct { + Repair []string `positional-arg-name:""` + } `positional-args:"yes"` +} + +var shortRepairHelp = i18n.G("Shows specific repairs") +var longRepairHelp = i18n.G(` +The repair command shows the details about one or multiple repairs. +`) + +func init() { + cmd := addCommand("repair", shortRepairHelp, longRepairHelp, func() flags.Commander { + return &cmdShowRepair{} + }, nil, nil) + if release.OnClassic { + cmd.hidden = true + } +} + +func (x *cmdShowRepair) Execute(args []string) error { + return runSnapRepair("show", x.Positional.Repair) +} + +type cmdListRepairs struct{} + +var shortRepairsHelp = i18n.G("Lists all repairs") +var longRepairsHelp = i18n.G(` +The repairs command lists all processed repairs for this device. +`) + +func init() { + cmd := addCommand("repairs", shortRepairsHelp, longRepairsHelp, func() flags.Commander { + return &cmdListRepairs{} + }, nil, nil) + if release.OnClassic { + cmd.hidden = true + } +} + +func (x *cmdListRepairs) Execute(args []string) error { + return runSnapRepair("list", args) +} diff -Nru snapd-2.28.5/cmd/snap/cmd_repair_repairs_test.go snapd-2.29.3/cmd/snap/cmd_repair_repairs_test.go --- snapd-2.28.5/cmd/snap/cmd_repair_repairs_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_repair_repairs_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,67 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main_test + +import ( + "os" + "path/filepath" + + . "gopkg.in/check.v1" + + snap "github.com/snapcore/snapd/cmd/snap" + "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/release" + "github.com/snapcore/snapd/testutil" +) + +func mockSnapRepair(c *C) *testutil.MockCmd { + coreLibExecDir := filepath.Join(dirs.GlobalRootDir, dirs.CoreLibExecDir) + err := os.MkdirAll(coreLibExecDir, 0755) + c.Assert(err, IsNil) + return testutil.MockCommand(c, filepath.Join(coreLibExecDir, "snap-repair"), "") +} + +func (s *SnapSuite) TestSnapShowRepair(c *C) { + restore := release.MockOnClassic(false) + defer restore() + + mockSnapRepair := mockSnapRepair(c) + defer mockSnapRepair.Restore() + + _, err := snap.Parser().ParseArgs([]string{"repair", "canonical-1"}) + c.Assert(err, IsNil) + c.Check(mockSnapRepair.Calls(), DeepEquals, [][]string{ + {"snap-repair", "show", "canonical-1"}, + }) +} + +func (s *SnapSuite) TestSnapListRepairs(c *C) { + restore := release.MockOnClassic(false) + defer restore() + + mockSnapRepair := mockSnapRepair(c) + defer mockSnapRepair.Restore() + + _, err := snap.Parser().ParseArgs([]string{"repairs"}) + c.Assert(err, IsNil) + c.Check(mockSnapRepair.Calls(), DeepEquals, [][]string{ + {"snap-repair", "list"}, + }) +} diff -Nru snapd-2.28.5/cmd/snap/cmd_set.go snapd-2.29.3/cmd/snap/cmd_set.go --- snapd-2.28.5/cmd/snap/cmd_set.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_set.go 2017-10-27 12:23:38.000000000 +0000 @@ -54,9 +54,12 @@ addCommand("set", shortSetHelp, longSetHelp, func() flags.Commander { return &cmdSet{} }, nil, []argDesc{ { name: "", + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("The snap to configure (e.g. hello-world)"), }, { + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Configuration value (key=value)"), }, }) diff -Nru snapd-2.28.5/cmd/snap/cmd_shell.go snapd-2.29.3/cmd/snap/cmd_shell.go --- snapd-2.28.5/cmd/snap/cmd_shell.go 2017-01-16 06:45:53.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_shell.go 2017-10-27 12:23:38.000000000 +0000 @@ -45,7 +45,9 @@ func() flags.Commander { return &cmdShell{} }, nil, []argDesc{{ + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("The type of shell you want"), }}) } diff -Nru snapd-2.28.5/cmd/snap/cmd_sign_build.go snapd-2.29.3/cmd/snap/cmd_sign_build.go --- snapd-2.28.5/cmd/snap/cmd_sign_build.go 2016-12-08 15:14:07.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_sign_build.go 2017-10-27 12:23:38.000000000 +0000 @@ -58,7 +58,9 @@ "k": i18n.G("Name of the GnuPG key to use (defaults to 'default' as key name)"), "grade": i18n.G("Grade states the build quality of the snap (defaults to 'stable')"), }, []argDesc{{ + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Filename of the snap you want to assert a build for"), }}) cmd.hidden = true diff -Nru snapd-2.28.5/cmd/snap/cmd_snap_op.go snapd-2.29.3/cmd/snap/cmd_snap_op.go --- snapd-2.28.5/cmd/snap/cmd_snap_op.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_snap_op.go 2017-10-23 06:17:27.000000000 +0000 @@ -71,7 +71,7 @@ } func wait(cli *client.Client, id string) (*client.Change, error) { - pb := progress.NewTextProgress() + pb := progress.MakeProgressBar() defer func() { pb.Finished() }() @@ -122,7 +122,7 @@ case t.ID == lastID: pb.Set(float64(t.Progress.Done)) default: - pb.Start(t.Progress.Label, float64(t.Progress.Total)) + pb.Start(t.Summary, float64(t.Progress.Total)) lastID = t.ID } break diff -Nru snapd-2.28.5/cmd/snap/cmd_snap_op_test.go snapd-2.29.3/cmd/snap/cmd_snap_op_test.go --- snapd-2.28.5/cmd/snap/cmd_snap_op_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_snap_op_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -26,7 +26,6 @@ "mime/multipart" "net/http" "net/http/httptest" - "os" "path/filepath" "regexp" "time" @@ -35,6 +34,9 @@ "github.com/snapcore/snapd/client" snap "github.com/snapcore/snapd/cmd/snap" + "github.com/snapcore/snapd/progress" + "github.com/snapcore/snapd/progress/progresstest" + "github.com/snapcore/snapd/testutil" ) type snapOpTestServer struct { @@ -103,6 +105,8 @@ } func (s *SnapOpSuite) TestWait(c *check.C) { + meter := &progresstest.Meter{} + defer progress.MockMeter(meter)() restore := snap.MockMaxGoneTime(time.Millisecond) defer restore() @@ -111,27 +115,16 @@ snap.ClientConfig.BaseURL = server.URL server.Close() - d := c.MkDir() - oldStdout := os.Stdout - stdout, err := ioutil.TempFile(d, "stdout") - c.Assert(err, check.IsNil) - defer func() { - os.Stdout = oldStdout - stdout.Close() - os.Remove(stdout.Name()) - }() - os.Stdout = stdout - cli := snap.Client() chg, err := snap.Wait(cli, "x") c.Assert(chg, check.IsNil) c.Assert(err, check.NotNil) - buf, err := ioutil.ReadFile(stdout.Name()) - c.Assert(err, check.IsNil) - c.Check(string(buf), check.Matches, "(?ms).*Waiting for server to restart.*") + c.Check(meter.Labels, testutil.Contains, "Waiting for server to restart") } func (s *SnapOpSuite) TestWaitRecovers(c *check.C) { + meter := &progresstest.Meter{} + defer progress.MockMeter(meter)() restore := snap.MockMaxGoneTime(time.Millisecond) defer restore() @@ -144,27 +137,14 @@ fmt.Fprintln(w, `{"type": "sync", "result": {"ready": true, "status": "Done"}}`) }) - d := c.MkDir() - oldStdout := os.Stdout - stdout, err := ioutil.TempFile(d, "stdout") - c.Assert(err, check.IsNil) - defer func() { - os.Stdout = oldStdout - stdout.Close() - os.Remove(stdout.Name()) - }() - os.Stdout = stdout - cli := snap.Client() chg, err := snap.Wait(cli, "x") // we got the change c.Assert(chg, check.NotNil) c.Assert(err, check.IsNil) - buf, err := ioutil.ReadFile(stdout.Name()) - c.Assert(err, check.IsNil) // but only after recovering - c.Check(string(buf), check.Matches, "(?ms).*Waiting for server to restart.*") + c.Check(meter.Labels, testutil.Contains, "Waiting for server to restart") } func (s *SnapOpSuite) TestInstall(c *check.C) { diff -Nru snapd-2.28.5/cmd/snap/cmd_unalias.go snapd-2.29.3/cmd/snap/cmd_unalias.go --- snapd-2.28.5/cmd/snap/cmd_unalias.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_unalias.go 2017-10-27 12:23:38.000000000 +0000 @@ -27,7 +27,7 @@ type cmdUnalias struct { Positionals struct { - AliasOrSnap string `required:"yes"` + AliasOrSnap aliasOrSnap `required:"yes"` } `positional-args:"true"` } @@ -40,6 +40,7 @@ addCommand("unalias", shortUnaliasHelp, longUnaliasHelp, func() flags.Commander { return &cmdUnalias{} }, nil, []argDesc{ + // TRANSLATORS: This needs to be wrapped in <>s. {name: i18n.G("")}, }) } @@ -50,7 +51,7 @@ } cli := Client() - id, err := cli.Unalias(x.Positionals.AliasOrSnap) + id, err := cli.Unalias(string(x.Positionals.AliasOrSnap)) if err != nil { return err } diff -Nru snapd-2.28.5/cmd/snap/cmd_userd_test.go snapd-2.29.3/cmd/snap/cmd_userd_test.go --- snapd-2.28.5/cmd/snap/cmd_userd_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_userd_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -27,16 +27,33 @@ . "gopkg.in/check.v1" snap "github.com/snapcore/snapd/cmd/snap" + "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/testutil" ) type userdSuite struct { BaseSnapSuite testutil.DBusTest + + restoreLogger func() } var _ = Suite(&userdSuite{}) +func (s *userdSuite) SetUpTest(c *C) { + s.BaseSnapSuite.SetUpTest(c) + s.DBusTest.SetUpTest(c) + + _, s.restoreLogger = logger.MockLogger() +} + +func (s *userdSuite) TearDownTest(c *C) { + s.BaseSnapSuite.TearDownTest(c) + s.DBusTest.TearDownTest(c) + + s.restoreLogger() +} + func (s *userdSuite) TestUserdBadCommandline(c *C) { _, err := snap.Parser().ParseArgs([]string{"userd", "extra-arg"}) c.Assert(err, ErrorMatches, "too many arguments for command") diff -Nru snapd-2.28.5/cmd/snap/cmd_watch_test.go snapd-2.29.3/cmd/snap/cmd_watch_test.go --- snapd-2.28.5/cmd/snap/cmd_watch_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/cmd_watch_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -21,15 +21,14 @@ import ( "fmt" - "io/ioutil" "net/http" - "os" "time" . "gopkg.in/check.v1" snap "github.com/snapcore/snapd/cmd/snap" - "github.com/snapcore/snapd/testutil" + "github.com/snapcore/snapd/progress" + "github.com/snapcore/snapd/progress/progresstest" ) var fmtWatchChangeJSON = `{"type": "sync", "result": { @@ -42,6 +41,8 @@ }}` func (s *SnapSuite) TestCmdWatch(c *C) { + meter := &progresstest.Meter{} + defer progress.MockMeter(meter)() restore := snap.MockMaxGoneTime(time.Millisecond) defer restore() @@ -64,22 +65,9 @@ n++ }) - oldStdout := os.Stdout - stdout, err := ioutil.TempFile("", "stdout") - c.Assert(err, IsNil) - defer func() { - os.Stdout = oldStdout - stdout.Close() - os.Remove(stdout.Name()) - }() - os.Stdout = stdout - - _, err = snap.Parser().ParseArgs([]string{"watch", "42"}) - os.Stdout = oldStdout + _, err := snap.Parser().ParseArgs([]string{"watch", "42"}) c.Assert(err, IsNil) c.Check(n, Equals, 3) - buf, err := ioutil.ReadFile(stdout.Name()) - c.Assert(err, IsNil) - c.Check(string(buf), testutil.Contains, "\rmy-snap 0 B / 100.00 KB") + c.Check(meter.Values, DeepEquals, []float64{51200}) } diff -Nru snapd-2.28.5/cmd/snap/complete.go snapd-2.29.3/cmd/snap/complete.go --- snapd-2.28.5/cmd/snap/complete.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/complete.go 2017-10-23 06:17:27.000000000 +0000 @@ -1,13 +1,36 @@ +// -*- 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 . + * + */ + package main import ( + "bufio" "fmt" + "os" "strings" "github.com/jessevdk/go-flags" "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/client" + "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/snap" ) type installedSnapName string @@ -28,9 +51,46 @@ return ret } +func completeFromSortedFile(filename, match string) ([]flags.Completion, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + defer file.Close() + + var ret []flags.Completion + + // TODO: look into implementing binary search + // e.g. https://github.com/pts/pts-line-bisect/ + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if line < match { + continue + } + if !strings.HasPrefix(line, match) { + break + } + ret = append(ret, flags.Completion{Item: line}) + if len(ret) > 10000 { + // too many matches; slow machines could take too long to process this + // e.g. the bbb takes ~1s to process ~2M entries (i.e. to reach the + // point of asking the user if they actually want to see that many + // results). 10k ought to be enough for anybody. + break + } + } + + return ret, nil +} + type remoteSnapName string func (s remoteSnapName) Complete(match string) []flags.Completion { + if ret, err := completeFromSortedFile(dirs.SnapNamesFile, match); err == nil { + return ret + } + if len(match) < 3 { return nil } @@ -323,6 +383,30 @@ return ret } +type appName string + +func (s appName) Complete(match string) []flags.Completion { + cli := Client() + apps, err := cli.Apps(nil, client.AppOptions{}) + if err != nil { + return nil + } + + var ret []flags.Completion + for _, app := range apps { + if app.IsService() { + continue + } + name := snap.JoinSnapApp(app.Snap, app.Name) + if !strings.HasPrefix(name, match) { + continue + } + ret = append(ret, flags.Completion{Item: name}) + } + + return ret +} + type serviceName string func (s serviceName) Complete(match string) []flags.Completion { @@ -347,3 +431,27 @@ return ret } + +type aliasOrSnap string + +func (s aliasOrSnap) Complete(match string) []flags.Completion { + aliases, err := Client().Aliases() + if err != nil { + return nil + } + var ret []flags.Completion + for snap, aliases := range aliases { + if strings.HasPrefix(snap, match) { + ret = append(ret, flags.Completion{Item: snap}) + } + for alias, status := range aliases { + if status.Status == "disabled" { + continue + } + if strings.HasPrefix(alias, match) { + ret = append(ret, flags.Completion{Item: alias}) + } + } + } + return ret +} diff -Nru snapd-2.28.5/cmd/snap/export_test.go snapd-2.29.3/cmd/snap/export_test.go --- snapd-2.28.5/cmd/snap/export_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -40,6 +40,7 @@ IsReexeced = isReexeced MaybePrintServices = maybePrintServices MaybePrintCommands = maybePrintCommands + SortByPath = sortByPath ) func MockPollTime(d time.Duration) (restore func()) { @@ -127,3 +128,11 @@ func AssertTypeNameCompletion(match string) []flags.Completion { return assertTypeName("").Complete(match) } + +func MockIsTerminal(t bool) (restore func()) { + oldIsTerminal := isTerminal + isTerminal = func() bool { return t } + return func() { + isTerminal = oldIsTerminal + } +} diff -Nru snapd-2.28.5/cmd/snap/last.go snapd-2.29.3/cmd/snap/last.go --- snapd-2.28.5/cmd/snap/last.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/cmd/snap/last.go 2017-10-27 12:23:38.000000000 +0000 @@ -38,7 +38,9 @@ } var changeIDMixinArgDesc = []argDesc{{ + // TRANSLATORS: This needs to be wrapped in <>s. name: i18n.G(""), + // TRANSLATORS: This should probably not start with a lowercase letter. desc: i18n.G("Change ID"), }} diff -Nru snapd-2.28.5/cmd/snap/main.go snapd-2.29.3/cmd/snap/main.go --- snapd-2.28.5/cmd/snap/main.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/main.go 2017-10-27 12:23:38.000000000 +0000 @@ -26,6 +26,7 @@ "path/filepath" "strings" "unicode" + "unicode/utf8" "github.com/jessevdk/go-flags" @@ -38,19 +39,31 @@ "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" + "github.com/snapcore/snapd/snap" ) func init() { // set User-Agent for when 'snap' talks to the store directly (snap download etc...) httputil.SetUserAgentFromVersion(cmd.Version, "snap") + + // plug/slot sanitization not used nor possible from snap command, make it no-op + snap.SanitizePlugsSlots = func(snapInfo *snap.Info) {} + + if osutil.GetenvBool("SNAPD_DEBUG") || osutil.GetenvBool("SNAPPY_TESTING") { + // in tests or when debugging, enforce the "tidy" lint checks + noticef = logger.Panicf + } } -// Standard streams, redirected for testing. var ( - Stdin io.Reader = os.Stdin - Stdout io.Writer = os.Stdout - Stderr io.Writer = os.Stderr - ReadPassword = terminal.ReadPassword + // Standard streams, redirected for testing. + Stdin io.Reader = os.Stdin + Stdout io.Writer = os.Stdout + Stderr io.Writer = os.Stderr + // overridden for testing + ReadPassword = terminal.ReadPassword + // set to logger.Panicf in testing + noticef = logger.Noticef ) type options struct { @@ -124,8 +137,15 @@ logger.Panicf("description of %s's %q of %q set from tag (=> no i18n)", cmdName, optName, origDesc) } if len(desc) > 0 { - if !unicode.IsUpper(([]rune)(desc)[0]) { - logger.Panicf("description of %s's %q not uppercase: %q", cmdName, optName, desc) + // decode the first rune instead of converting all of desc into []rune + r, _ := utf8.DecodeRuneInString(desc) + // note IsLower != !IsUpper for runes with no upper/lower. + // Also note that login.u.c. is the only exception we're allowing for + // now, but the list of exceptions could grow -- if it does, we might + // want to change it to check for urlish things instead of just + // login.u.c. + if unicode.IsLower(r) && !strings.HasPrefix(desc, "login.ubuntu.com") { + noticef("description of %s's %q is lowercase: %q", cmdName, optName, desc) } } } @@ -133,7 +153,7 @@ func lintArg(cmdName, optName, desc, origDesc string) { lintDesc(cmdName, optName, desc, origDesc) if optName[0] != '<' || optName[len(optName)-1] != '>' { - logger.Panicf("argument %q's %q should have <>s", cmdName, optName) + noticef("argument %q's %q should be wrapped in <>s", cmdName, optName) } } diff -Nru snapd-2.28.5/cmd/snap/main_test.go snapd-2.29.3/cmd/snap/main_test.go --- snapd-2.28.5/cmd/snap/main_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap/main_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -37,6 +37,7 @@ "github.com/snapcore/snapd/cmd" "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/osutil" + snapdsnap "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/testutil" snap "github.com/snapcore/snapd/cmd/snap" @@ -74,6 +75,8 @@ snap.ReadPassword = s.readPassword s.AuthFile = filepath.Join(c.MkDir(), "json") os.Setenv(TestAuthFileEnvKey, s.AuthFile) + + snapdsnap.MockSanitizePlugsSlots(func(snapInfo *snapdsnap.Info) {}) } func (s *BaseSnapSuite) TearDownTest(c *C) { diff -Nru snapd-2.28.5/cmd/snap-confine/apparmor-support.c snapd-2.29.3/cmd/snap-confine/apparmor-support.c --- snapd-2.28.5/cmd/snap-confine/apparmor-support.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/apparmor-support.c 2017-10-23 06:17:27.000000000 +0000 @@ -73,7 +73,7 @@ // Use aa_getcon() to check the label of the current process and // confinement type. Note that the returned label must be released with // free() but the mode is a constant string that must not be freed. - char *label __attribute__ ((cleanup(sc_cleanup_string))) = NULL; + char *label SC_CLEANUP(sc_cleanup_string) = NULL; char *mode = NULL; if (aa_getcon(&label, &mode) < 0) { die("cannot query current apparmor profile"); diff -Nru snapd-2.28.5/cmd/snap-confine/cookie-support.c snapd-2.29.3/cmd/snap-confine/cookie-support.c --- snapd-2.28.5/cmd/snap-confine/cookie-support.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/cookie-support.c 2017-10-23 06:17:27.000000000 +0000 @@ -39,13 +39,13 @@ char *sc_cookie_get_from_snapd(const char *snap_name, struct sc_error **errorp) { - char context_path[PATH_MAX]; + char context_path[PATH_MAX] = { 0 }; struct sc_error *err = NULL; char *context = NULL; sc_must_snprintf(context_path, sizeof(context_path), "%s/snap.%s", sc_cookie_dir, snap_name); - int fd __attribute__ ((cleanup(sc_cleanup_close))) = -1; + int fd SC_CLEANUP(sc_cleanup_close) = -1; fd = open(context_path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); if (fd < 0) { err = @@ -55,7 +55,7 @@ goto out; } // large enough buffer for opaque cookie string - char context_val[255]; + char context_val[255] = { 0 }; ssize_t n = read(fd, context_val, sizeof(context_val) - 1); if (n < 0) { err = diff -Nru snapd-2.28.5/cmd/snap-confine/cookie-support-test.c snapd-2.29.3/cmd/snap-confine/cookie-support-test.c --- snapd-2.28.5/cmd/snap-confine/cookie-support-test.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/cookie-support-test.c 2017-10-23 06:17:27.000000000 +0000 @@ -47,7 +47,7 @@ static void create_dumy_cookie_file(const char *snap_name, const char *dummy_cookie) { - char path[PATH_MAX]; + char path[PATH_MAX] = { 0 }; FILE *f; int n; diff -Nru snapd-2.28.5/cmd/snap-confine/mount-support.c snapd-2.29.3/cmd/snap-confine/mount-support.c --- snapd-2.28.5/cmd/snap-confine/mount-support.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/mount-support.c 2017-10-23 06:17:27.000000000 +0000 @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -136,76 +139,50 @@ sc_do_mount("/dev/pts/ptmx", "/dev/ptmx", "none", MS_BIND, 0); } -/* - * Setup mount profiles as described by snapd. - * - * This function reads /var/lib/snapd/mount/$security_tag.fstab as a fstab(5) file - * and executes the mount requests described there. - * - * Currently only bind mounts are allowed. All bind mounts are read only by - * default though the `rw` flag can be used. +/** + * Setup mount profiles by running snap-update-ns. * - * This function is called with the rootfs being "consistent" so that it is - * either the core snap on an all-snap system or the core snap + punched holes - * on a classic system. + * The first argument is an open file descriptor (though opened with O_PATH, so + * not as powerful), to a copy of snap-update-ns. The program is opened before + * the root filesystem is pivoted so that it is easier to pick the right copy. **/ -static void sc_setup_mount_profiles(const char *snap_name) +static void sc_setup_mount_profiles(int snap_update_ns_fd, + const char *snap_name) { - debug("%s: %s", __FUNCTION__, snap_name); - - FILE *desired __attribute__ ((cleanup(sc_cleanup_endmntent))) = NULL; - FILE *current __attribute__ ((cleanup(sc_cleanup_endmntent))) = NULL; - char profile_path[PATH_MAX]; - - sc_must_snprintf(profile_path, sizeof(profile_path), - "/run/snapd/ns/snap.%s.fstab", snap_name); - debug("opening current mount profile %s", profile_path); - current = setmntent(profile_path, "w"); - if (current == NULL) { - die("cannot open current mount profile: %s", profile_path); - } - - sc_must_snprintf(profile_path, sizeof(profile_path), - "/var/lib/snapd/mount/snap.%s.fstab", snap_name); - debug("opening desired mount profile %s", profile_path); - desired = setmntent(profile_path, "r"); - if (desired == NULL && errno == ENOENT) { - // It is ok for the desired profile to not exist. Note that in this - // case we also "update" the current profile as we already opened and - // truncated it above. - return; - } - if (desired == NULL) { - die("cannot open desired mount profile: %s", profile_path); - } - - struct mntent *m = NULL; - while ((m = getmntent(desired)) != NULL) { - debug("read mount entry\n" - "\tmnt_fsname: %s\n" - "\tmnt_dir: %s\n" - "\tmnt_type: %s\n" - "\tmnt_opts: %s\n" - "\tmnt_freq: %d\n" - "\tmnt_passno: %d", - m->mnt_fsname, m->mnt_dir, m->mnt_type, - m->mnt_opts, m->mnt_freq, m->mnt_passno); - int flags = MS_BIND | MS_RDONLY | MS_NODEV | MS_NOSUID; - debug("initial flags are: bind,ro,nodev,nosuid"); - if (strcmp(m->mnt_type, "none") != 0) { - die("cannot honor mount profile, only 'none' filesystem type is supported"); - } - if (hasmntopt(m, "bind") == NULL) { - die("cannot honor mount profile, the bind mount flag is mandatory"); - } - if (hasmntopt(m, "rw") != NULL) { - flags &= ~MS_RDONLY; - } - sc_do_mount(m->mnt_fsname, m->mnt_dir, NULL, flags, NULL); - if (addmntent(current, m) != 0) { // NOTE: returns 1 on error. - die("cannot append entry to the current mount profile"); + debug("calling snap-update-ns to initialize mount namespace"); + pid_t child = fork(); + if (child < 0) { + die("cannot fork to run snap-update-ns"); + } + if (child == 0) { + // We are the child, execute snap-update-ns + char *snap_name_copy SC_CLEANUP(sc_cleanup_string) = NULL; + snap_name_copy = strdup(snap_name); + if (snap_name_copy == NULL) { + die("cannot copy snap name"); } + char *argv[] = { + "snap-update-ns", "--from-snap-confine", snap_name_copy, + NULL + }; + char *envp[] = { NULL }; + debug("fexecv(%d (snap-update-ns), %s %s %s,)", + snap_update_ns_fd, argv[0], argv[1], argv[2]); + fexecve(snap_update_ns_fd, argv, envp); + die("cannot execute snap-update-ns"); + } + // We are the parent, so wait for snap-update-ns to finish. + int status = 0; + debug("waiting for snap-update-ns to finish..."); + if (waitpid(child, &status, 0) < 0) { + die("waitpid() failed for snap-update-ns process"); + } + if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { + die("snap-update-ns failed with code %i", WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + die("snap-update-ns killed by signal %i", WTERMSIG(status)); } + debug("snap-update-ns finished successfully"); } struct sc_mount { @@ -259,8 +236,8 @@ static void sc_bootstrap_mount_namespace(const struct sc_mount_config *config) { char scratch_dir[] = "/tmp/snap.rootfs_XXXXXX"; - char src[PATH_MAX]; - char dst[PATH_MAX]; + char src[PATH_MAX] = { 0 }; + char dst[PATH_MAX] = { 0 }; if (mkdtemp(scratch_dir) == NULL) { die("cannot create temporary directory for the root file system"); } @@ -363,7 +340,7 @@ // where $ROOT is either "/" or the "/snap/core/current" // that we are re-execing from char *src = NULL; - char self[PATH_MAX + 1] = { 0, }; + char self[PATH_MAX + 1] = { 0 }; if (readlink("/proc/self/exe", self, sizeof(self) - 1) < 0) { die("cannot read /proc/self/exe"); } @@ -555,16 +532,49 @@ return false; } +static int sc_open_snap_update_ns() +{ + // +1 is for the case where the link is exactly PATH_MAX long but we also + // want to store the terminating '\0'. The readlink system call doesn't add + // terminating null, but our initialization of buf handles this for us. + char buf[PATH_MAX + 1] = { 0 }; + if (readlink("/proc/self/exe", buf, sizeof buf) < 0) { + die("cannot readlink /proc/self/exe"); + } + if (buf[0] != '/') { // this shouldn't happen, but make sure have absolute path + die("readlink /proc/self/exe returned relative path"); + } + char *bufcopy SC_CLEANUP(sc_cleanup_string) = NULL; + bufcopy = strdup(buf); + if (bufcopy == NULL) { + die("cannot copy buffer"); + } + char *dname = dirname(bufcopy); + sc_must_snprintf(buf, sizeof buf, "%s/%s", dname, "snap-update-ns"); + debug("snap-update-ns executable: %s", buf); + int fd = open(buf, O_PATH | O_RDONLY | O_NOFOLLOW | O_CLOEXEC); + if (fd < 0) { + die("cannot open snap-update-ns executable"); + } + debug("opened snap-update-ns executable as file descriptor %d", fd); + return fd; +} + void sc_populate_mount_ns(const char *base_snap_name, const char *snap_name) { // Get the current working directory before we start fiddling with // mounts and possibly pivot_root. At the end of the whole process, we // will try to re-locate to the same directory (if possible). - char *vanilla_cwd __attribute__ ((cleanup(sc_cleanup_string))) = NULL; + char *vanilla_cwd SC_CLEANUP(sc_cleanup_string) = NULL; vanilla_cwd = get_current_dir_name(); if (vanilla_cwd == NULL) { die("cannot get the current working directory"); } + // Find and open snap-update-ns from the same path as where we + // (snap-confine) were called. + int snap_update_ns_fd SC_CLEANUP(sc_cleanup_close) = -1; + snap_update_ns_fd = sc_open_snap_update_ns(); + bool on_classic_distro = is_running_on_classic_distribution(); // on classic or with alternative base snaps we need to setup // a different confinement @@ -592,18 +602,24 @@ {"/run/netns", true}, // access to the 'ip netns' network namespaces {}, }; - char rootfs_dir[PATH_MAX]; - again: + char rootfs_dir[PATH_MAX] = { 0 }; sc_must_snprintf(rootfs_dir, sizeof rootfs_dir, "%s/%s/current/", SNAP_MOUNT_DIR, base_snap_name); if (access(rootfs_dir, F_OK) != 0) { if (sc_streq(base_snap_name, "core")) { - // As a special fallback, allow the base snap to degrade from - // "core" to "ubuntu-core". This is needed for the migration - // tests. + // As a special fallback, allow the + // base snap to degrade from "core" to + // "ubuntu-core". This is needed for + // the migration tests. base_snap_name = "ubuntu-core"; - goto again; + sc_must_snprintf(rootfs_dir, sizeof rootfs_dir, + "%s/%s/current/", + SNAP_MOUNT_DIR, + base_snap_name); + if (access(rootfs_dir, F_OK) != 0) { + die("cannot locate the core or legacy core snap (current symlink missing?)"); + } } die("cannot locate the base snap: %s", base_snap_name); } @@ -646,7 +662,7 @@ sc_setup_quirks(); } // setup the security backend bind mounts - sc_setup_mount_profiles(snap_name); + sc_setup_mount_profiles(snap_update_ns_fd, snap_name); // Try to re-locate back to vanilla working directory. This can fail // because that directory is no longer present. @@ -665,8 +681,7 @@ static bool is_mounted_with_shared_option(const char *dir) { - struct sc_mountinfo *sm - __attribute__ ((cleanup(sc_cleanup_mountinfo))) = NULL; + struct sc_mountinfo *sm SC_CLEANUP(sc_cleanup_mountinfo) = NULL; sm = sc_parse_mountinfo(NULL); if (sm == NULL) { die("cannot parse /proc/self/mountinfo"); diff -Nru snapd-2.28.5/cmd/snap-confine/mount-support-nvidia.c snapd-2.29.3/cmd/snap-confine/mount-support-nvidia.c --- snapd-2.28.5/cmd/snap-confine/mount-support-nvidia.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/mount-support-nvidia.c 2017-10-23 06:17:27.000000000 +0000 @@ -33,7 +33,9 @@ #include "../libsnap-confine-private/string-utils.h" #include "../libsnap-confine-private/utils.h" -#ifdef NVIDIA_ARCH +#define SC_NVIDIA_DRIVER_VERSION_FILE "/sys/module/nvidia/version" + +#ifdef NVIDIA_BIARCH // List of globs that describe nvidia userspace libraries. // This list was compiled from the following packages. @@ -93,7 +95,7 @@ const char *glob_list[], size_t glob_list_len) { - glob_t glob_res __attribute__ ((__cleanup__(globfree))) = { + glob_t glob_res SC_CLEANUP(globfree) = { .gl_pathv = NULL}; // Find all the entries matching the list of globs for (size_t i = 0; i < glob_list_len; ++i) { @@ -109,12 +111,11 @@ } // Symlink each file found for (size_t i = 0; i < glob_res.gl_pathc; ++i) { - char symlink_name[512]; - char symlink_target[512]; + char symlink_name[512] = { 0 }; + char symlink_target[512] = { 0 }; const char *pathname = glob_res.gl_pathv[i]; char *pathname_copy - __attribute__ ((cleanup(sc_cleanup_string))) = - strdup(pathname); + SC_CLEANUP(sc_cleanup_string) = strdup(pathname); char *filename = basename(pathname_copy); struct stat stat_buf; int err = lstat(pathname, &stat_buf); @@ -166,10 +167,10 @@ } } -static void sc_mount_nvidia_driver_arch(const char *rootfs_dir) +static void sc_mount_nvidia_driver_biarch(const char *rootfs_dir) { // Bind mount a tmpfs on $rootfs_dir/var/lib/snapd/lib/gl - char buf[512]; + char buf[512] = { 0 }; sc_must_snprintf(buf, sizeof(buf), "%s%s", rootfs_dir, "/var/lib/snapd/lib/gl"); const char *libgl_dir = buf; @@ -187,21 +188,20 @@ } } -#endif // ifdef NVIDIA_ARCH +#endif // ifdef NVIDIA_BIARCH -#ifdef NVIDIA_UBUNTU +#ifdef NVIDIA_MULTIARCH struct sc_nvidia_driver { int major_version; int minor_version; }; -#define SC_NVIDIA_DRIVER_VERSION_FILE "/sys/module/nvidia/version" #define SC_LIBGL_DIR "/var/lib/snapd/lib/gl" static void sc_probe_nvidia_driver(struct sc_nvidia_driver *driver) { - FILE *file __attribute__ ((cleanup(sc_cleanup_file))) = NULL; + FILE *file SC_CLEANUP(sc_cleanup_file) = NULL; debug("opening file describing nvidia driver version"); file = fopen(SC_NVIDIA_DRIVER_VERSION_FILE, "rt"); if (file == NULL) { @@ -224,7 +224,7 @@ driver->minor_version); } -static void sc_mount_nvidia_driver_ubuntu(const char *rootfs_dir) +static void sc_mount_nvidia_driver_multiarch(const char *rootfs_dir) { struct sc_nvidia_driver driver; @@ -237,7 +237,8 @@ } // Construct the paths for the driver userspace libraries // and for the gl directory. - char src[PATH_MAX], dst[PATH_MAX]; + char src[PATH_MAX] = { 0 }; + char dst[PATH_MAX] = { 0 }; sc_must_snprintf(src, sizeof src, "/usr/lib/nvidia-%d", driver.major_version); sc_must_snprintf(dst, sizeof dst, "%s%s", rootfs_dir, SC_LIBGL_DIR); @@ -256,14 +257,18 @@ die("cannot bind mount nvidia driver %s -> %s", src, dst); } } -#endif // ifdef NVIDIA_UBUNTU +#endif // ifdef NVIDIA_MULTIARCH void sc_mount_nvidia_driver(const char *rootfs_dir) { -#ifdef NVIDIA_UBUNTU - sc_mount_nvidia_driver_ubuntu(rootfs_dir); -#endif // ifdef NVIDIA_UBUNTU -#ifdef NVIDIA_ARCH - sc_mount_nvidia_driver_arch(rootfs_dir); -#endif // ifdef NVIDIA_ARCH + /* If NVIDIA module isn't loaded, don't attempt to mount the drivers */ + if (access(SC_NVIDIA_DRIVER_VERSION_FILE, F_OK) != 0) { + return; + } +#ifdef NVIDIA_MULTIARCH + sc_mount_nvidia_driver_multiarch(rootfs_dir); +#endif // ifdef NVIDIA_MULTIARCH +#ifdef NVIDIA_BIARCH + sc_mount_nvidia_driver_biarch(rootfs_dir); +#endif // ifdef NVIDIA_BIARCH } diff -Nru snapd-2.28.5/cmd/snap-confine/ns-support.c snapd-2.29.3/cmd/snap-confine/ns-support.c --- snapd-2.28.5/cmd/snap-confine/ns-support.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/ns-support.c 2017-10-23 06:17:27.000000000 +0000 @@ -78,8 +78,7 @@ **/ static bool sc_is_ns_group_dir_private() { - struct sc_mountinfo *info - __attribute__ ((cleanup(sc_cleanup_mountinfo))) = NULL; + struct sc_mountinfo *info SC_CLEANUP(sc_cleanup_mountinfo) = NULL; info = sc_parse_mountinfo(NULL); if (info == NULL) { die("cannot parse /proc/self/mountinfo"); @@ -101,8 +100,8 @@ void sc_reassociate_with_pid1_mount_ns() { - int init_mnt_fd __attribute__ ((cleanup(sc_cleanup_close))) = -1; - int self_mnt_fd __attribute__ ((cleanup(sc_cleanup_close))) = -1; + int init_mnt_fd SC_CLEANUP(sc_cleanup_close) = -1; + int self_mnt_fd SC_CLEANUP(sc_cleanup_close) = -1; debug("checking if the current process shares mount namespace" " with the init process"); @@ -117,7 +116,8 @@ if (self_mnt_fd < 0) { die("cannot open mount namespace of the current process (O_PATH)"); } - char init_buf[128], self_buf[128]; + char init_buf[128] = { 0 }; + char self_buf[128] = { 0 }; memset(init_buf, 0, sizeof init_buf); if (readlinkat(init_mnt_fd, "", init_buf, sizeof init_buf) < 0) { if (errno == ENOENT) { @@ -141,8 +141,7 @@ "the init process, re-association required"); // NOTE: we cannot use O_NOFOLLOW here because that file will always be a // symbolic link. We actually want to open it this way. - int init_mnt_fd_real - __attribute__ ((cleanup(sc_cleanup_close))) = -1; + int init_mnt_fd_real SC_CLEANUP(sc_cleanup_close) = -1; init_mnt_fd_real = open("/proc/1/ns/mnt", O_RDONLY | O_CLOEXEC); if (init_mnt_fd_real < 0) { die("cannot open mount namespace of the init process"); @@ -244,10 +243,10 @@ struct sc_apparmor *apparmor) { // Open the mount namespace file. - char mnt_fname[PATH_MAX]; + char mnt_fname[PATH_MAX] = { 0 }; sc_must_snprintf(mnt_fname, sizeof mnt_fname, "%s%s", group->name, SC_NS_MNT_FILE); - int mnt_fd __attribute__ ((cleanup(sc_cleanup_close))) = -1; + int mnt_fd SC_CLEANUP(sc_cleanup_close) = -1; // NOTE: There is no O_EXCL here because the file can be around but // doesn't have to be a mounted namespace. // @@ -276,8 +275,7 @@ #define NSFS_MAGIC 0x6e736673 #endif if (buf.f_type == NSFS_MAGIC || buf.f_type == PROC_SUPER_MAGIC) { - char *vanilla_cwd __attribute__ ((cleanup(sc_cleanup_string))) = - NULL; + char *vanilla_cwd SC_CLEANUP(sc_cleanup_string) = NULL; vanilla_cwd = get_current_dir_name(); if (vanilla_cwd == NULL) { die("cannot get the current working directory"); @@ -377,8 +375,8 @@ debug ("capturing mount namespace of process %d in namespace group %s", (int)parent, group->name); - char src[PATH_MAX]; - char dst[PATH_MAX]; + char src[PATH_MAX] = { 0 }; + char dst[PATH_MAX] = { 0 }; sc_must_snprintf(src, sizeof src, "/proc/%d/ns/mnt", (int)parent); sc_must_snprintf(dst, sizeof dst, "%s%s", group->name, @@ -438,7 +436,7 @@ void sc_discard_preserved_ns_group(struct sc_ns_group *group) { // Remember the current working directory - int old_dir_fd __attribute__ ((cleanup(sc_cleanup_close))) = -1; + int old_dir_fd SC_CLEANUP(sc_cleanup_close) = -1; old_dir_fd = open(".", O_PATH | O_DIRECTORY | O_CLOEXEC); if (old_dir_fd < 0) { die("cannot open current directory"); @@ -448,7 +446,7 @@ die("cannot move to namespace group directory"); } // Unmount ${group_name}.mnt which holds the preserved namespace - char mnt_fname[PATH_MAX]; + char mnt_fname[PATH_MAX] = { 0 }; sc_must_snprintf(mnt_fname, sizeof mnt_fname, "%s%s", group->name, SC_NS_MNT_FILE); debug("unmounting preserved mount namespace file %s", mnt_fname); diff -Nru snapd-2.28.5/cmd/snap-confine/quirks.c snapd-2.29.3/cmd/snap-confine/quirks.c --- snapd-2.28.5/cmd/snap-confine/quirks.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/quirks.c 2017-10-23 06:17:27.000000000 +0000 @@ -90,7 +90,7 @@ if (sc_nonfatal_mkpath(dest_dir, 0755) < 0) { die("cannot create empty directory at %s", dest_dir); } - char buf[1000]; + char buf[1000] = { 0 }; const char *flags_str = sc_mount_opt2str(buf, sizeof buf, flags); debug("performing operation: mount %s %s -o %s", src_dir, dest_dir, flags_str); @@ -130,15 +130,15 @@ } debug("bind-mounting all the files from the reference directory"); - DIR *dirp __attribute__ ((cleanup(sc_cleanup_closedir))) = NULL; + DIR *dirp SC_CLEANUP(sc_cleanup_closedir) = NULL; dirp = opendir(ref_dir); if (dirp == NULL) { die("cannot open reference directory %s", ref_dir); } struct dirent *entryp = NULL; do { - char src_name[PATH_MAX * 2]; - char dest_name[PATH_MAX * 2]; + char src_name[PATH_MAX * 2] = { 0 }; + char dest_name[PATH_MAX * 2] = { 0 }; // Set errno to zero, if readdir fails it will not only return null but // set errno to a non-zero value. This is how we can differentiate // end-of-directory from an actual error. @@ -203,7 +203,7 @@ "/var/lib/snapd", snapd_tmp); } // now let's make /var/lib the vanilla /var/lib from the core snap - char buf[PATH_MAX]; + char buf[PATH_MAX] = { 0 }; sc_must_snprintf(buf, sizeof buf, "%s/var/lib", sc_get_inner_core_mount_point()); sc_quirk_create_writable_mimic("/var/lib", buf, diff -Nru snapd-2.28.5/cmd/snap-confine/seccomp-support.c snapd-2.29.3/cmd/snap-confine/seccomp-support.c --- snapd-2.28.5/cmd/snap-confine/seccomp-support.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/seccomp-support.c 2017-10-27 12:31:06.000000000 +0000 @@ -38,7 +38,7 @@ static const char *filter_profile_dir = "/var/lib/snapd/seccomp/bpf/"; // MAX_BPF_SIZE is an arbitrary limit. -const int MAX_BPF_SIZE = 32 * 1024; +#define MAX_BPF_SIZE (32 * 1024) typedef struct sock_filter bpf_instr; @@ -66,7 +66,7 @@ die("valid_bpfpath_is_safe needs an absolute path as input"); } // strtok_r() modifies its first argument, so work on a copy - char *tokenized __attribute__ ((cleanup(sc_cleanup_string))) = NULL; + char *tokenized SC_CLEANUP(sc_cleanup_string) = NULL; tokenized = strdup(path); if (tokenized == NULL) { die("cannot allocate memory for copy of path"); @@ -74,7 +74,7 @@ // allocate a string large enough to hold path, and initialize it to // '/' size_t checked_path_size = strlen(path) + 1; - char *checked_path __attribute__ ((cleanup(sc_cleanup_string))) = NULL; + char *checked_path SC_CLEANUP(sc_cleanup_string) = NULL; checked_path = calloc(checked_path_size, 1); if (checked_path == NULL) { die("cannot allocate memory for checked_path"); @@ -93,7 +93,7 @@ // reconstruct the path from '/' down to profile_name char *buf_token = strtok_r(tokenized, "/", &buf_saveptr); while (buf_token != NULL) { - char *prev __attribute__ ((cleanup(sc_cleanup_string))) = NULL; + char *prev SC_CLEANUP(sc_cleanup_string) = NULL; prev = strdup(checked_path); // needed by vsnprintf in sc_must_snprintf if (prev == NULL) { die("cannot allocate memory for copy of checked_path"); @@ -116,7 +116,7 @@ { debug("loading bpf program for security tag %s", filter_profile); - char profile_path[PATH_MAX]; + char profile_path[PATH_MAX] = { 0 }; sc_must_snprintf(profile_path, sizeof(profile_path), "%s/%s.bin", filter_profile_dir, filter_profile); @@ -154,7 +154,7 @@ validate_bpfpath_is_safe(profile_path); // load bpf - unsigned char bpf[MAX_BPF_SIZE + 1]; // account for EOF + char bpf[MAX_BPF_SIZE + 1] = { 0 }; // account for EOF FILE *fp = fopen(profile_path, "rb"); if (fp == NULL) { die("cannot read %s", profile_path); @@ -170,6 +170,10 @@ fclose(fp); debug("read %zu bytes from %s", num_read, profile_path); + if (sc_streq(bpf, "@unrestricted\n")) { + return 0; + } + uid_t real_uid, effective_uid, saved_uid; if (getresuid(&real_uid, &effective_uid, &saved_uid) < 0) { die("cannot call getresuid"); diff -Nru snapd-2.28.5/cmd/snap-confine/snap-confine.apparmor.in snapd-2.29.3/cmd/snap-confine/snap-confine.apparmor.in --- snapd-2.28.5/cmd/snap-confine/snap-confine.apparmor.in 2017-10-11 17:40:25.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/snap-confine.apparmor.in 2017-11-09 12:09:26.000000000 +0000 @@ -2,6 +2,14 @@ #include @LIBEXECDIR@/snap-confine (attach_disconnected) { + # Include any additional files that snapd chose to generate. + # - for $HOME on NFS + # - for $HOME on encrypted media + # + # Those are discussed on https://forum.snapcraft.io/t/snapd-vs-upstream-kernel-vs-apparmor + # and https://forum.snapcraft.io/t/snaps-and-nfs-home/ + include "/var/lib/snapd/apparmor/snap-confine.d" + # We run privileged, so be fanatical about what we include and don't use # any abstractions /etc/ld.so.cache r, @@ -35,16 +43,24 @@ /dev/pts/[0-9]* rw, /dev/tty rw, - # cgroups + # cgroup: devices capability sys_admin, capability dac_override, /sys/fs/cgroup/devices/snap{,py}.*/ w, /sys/fs/cgroup/devices/snap{,py}.*/tasks w, /sys/fs/cgroup/devices/snap{,py}.*/devices.{allow,deny} w, + # cgroup: freezer + # Allow creating per-snap cgroup freezers and adding snap command (task) + # invocations to the freezer. This allows for reliably enumerating all + # running tasks for the snap. + /sys/fs/cgroup/freezer/ r, + /sys/fs/cgroup/freezer/snap.*/ w, + /sys/fs/cgroup/freezer/snap.*/tasks w, + # querying udev /etc/udev/udev.conf r, - /sys/devices/**/uevent r, + /sys/**/uevent r, /lib/udev/snappy-app-dev ixr, # drop /run/udev/** rw, /{,usr/}bin/tr ixr, @@ -96,9 +112,6 @@ # reading seccomp filters /{tmp/snap.rootfs_*/,}var/lib/snapd/seccomp/bpf/*.bin r, - # reading mount profiles - /{tmp/snap.rootfs_*/,}var/lib/snapd/mount/*.fstab r, - # ensuring correct permissions in sc_quirk_create_writable_mimic /{tmp/snap.rootfs_*/,}var/lib/ rw, @@ -200,6 +213,9 @@ umount /var/lib/snapd/hostfs/proc/, mount options=(rw rslave) -> /var/lib/snapd/hostfs/, + # Allow reading the os-release file (possibly a symlink to /usr/lib). + /{etc/,usr/lib/}os-release r, + # set up snap-specific private /tmp dir capability chown, /tmp/ w, @@ -224,28 +240,6 @@ # NOTE: at this stage the /snap directory is stable as we have called # pivot_root already. - # Support mount profiles via the content interface. This should correspond - # to permutations of $SNAP -> $SNAP for reading and $SNAP_{DATA,COMMON} -> - # $SNAP_{DATA,COMMON} for both reading and writing. - # - # Note that: - # /snap/*/*/** - # is meant to mean: - # /snap/$SNAP_NAME/$SNAP_REVISION/and-any-subdirectory - # but: - # /var/snap/*/** - # is meant to mean: - # /var/snap/$SNAP_NAME/$SNAP_REVISION/ - mount options=(ro bind) /snap/*/** -> /snap/*/*/**, - mount options=(ro bind) /snap/*/** -> /var/snap/*/**, - mount options=(rw bind) /var/snap/*/** -> /var/snap/*/**, - mount options=(ro bind) /var/snap/*/** -> /var/snap/*/**, - # But we don't want anyone to touch /snap/bin - audit deny mount /snap/bin/** -> /**, - audit deny mount /** -> /snap/bin/**, - # Allow the content interface to bind fonts from the host filesystem - mount options=(ro bind) /var/lib/snapd/hostfs/usr/share/fonts/ -> /snap/*/*/**, - # nvidia handling, glob needs /usr/** and the launcher must be # able to bind mount the nvidia dir /sys/module/nvidia/version r, @@ -336,7 +330,6 @@ /run/snapd/ns/ rw, /run/snapd/ns/*.lock rwk, /run/snapd/ns/*.mnt rw, - /run/snapd/ns/*.fstab rw, ptrace (read, readby, tracedby) peer=@LIBEXECDIR@/snap-confine//mount-namespace-capture-helper, @{PROC}/*/mountinfo r, capability sys_chroot, @@ -413,4 +406,120 @@ # Allow snap-confine to be killed signal (receive) peer=unconfined, + + # Allow executing snap-update-ns when... + + # ...snap-confine is, conceptually, re-executing and uses snap-update-ns + # from the distribution package. This is also the location used when using + # the core/base snap on all-snap systems. The variants here represent + # various locations of libexecdir across distributions. + /usr/lib{,exec,64}/snapd/snap-update-ns Cxr -> snap_update_ns, + + # ...snap-confine is not, conceptually, re-executing and uses + # snap-update-ns from the distribution package but we are already inside + # the constructed mount namespace so we must traverse "hostfs". The + # variants here represent various locations of libexecdir across + # distributions. + /var/lib/snapd/hostfs/usr/lib{,exec,64}/snapd/snap-update-ns Cxr -> snap_update_ns, + + # ..snap-confine is, conceptually, re-executing and uses snap-update-ns + # from the core snap. Note that the location of the core snap varies from + # distribution to distribution. The variants here represent different + # locations of snap mount directory across distributions. + /{,var/lib/snapd/}snap/core/*/usr/lib/snapd/snap-update-ns Cxr -> snap_update_ns, + + # ...snap-confine is, conceptually, re-executing and uses snap-update-ns + # from the core snap but we are already inside the constructed mount + # namespace. Here the apparmor kernel module re-constructs the path to + # snap-update-ns using the "hostfs" mount entry rather than the more + # "natural" /snap mount entry but we have no control over that. This is + # reported as (LP: #1716339). The variants here represent different + # locations of snap mount directory across distributions. + /var/lib/snapd/hostfs/{,var/lib/snapd/}snap/core/*/usr/lib/snapd/snap-update-ns Cxr -> snap_update_ns, + + profile snap_update_ns (attach_disconnected) { + # The next four rules mirror those above. We want to be able to read + # and map snap-update-ns into memory but it may come from a variety of places. + /usr/lib{,exec,64}/snapd/snap-update-ns mr, + /var/lib/snapd/hostfs/usr/lib{,exec,64}/snapd/snap-update-ns mr, + /{,var/lib/snapd/}snap/core/*/usr/lib/snapd/snap-update-ns mr, + /var/lib/snapd/hostfs/{,var/lib/snapd/}snap/core/*/usr/lib/snapd/snap-update-ns mr, + + # Allow reading the dynamic linker cache. + /etc/ld.so.cache r, + # Allow reading, mapping and executing the dynamic linker. + /{,usr/}lib{,32,64,x32}/{,@{multiarch}/}ld-*.so mrix, + # Allow reading and mapping various parts of the standard library and + # dynamically loaded nss modules and what not. + /{,usr/}lib{,32,64,x32}/{,@{multiarch}/}libc{,-[0-9]*}.so* mr, + /{,usr/}lib{,32,64,x32}/{,@{multiarch}/}libpthread{,-[0-9]*}.so* mr, + + # Allow reading the command line (snap-update-ns uses it in pre-Go bootstrap code). + @{PROC}/@{pid}/cmdline r, + + # Allow reading the os-release file (possibly a symlink to /usr/lib). + /{etc/,usr/lib/}os-release r, + + # Allow creating/grabbing various snapd lock files. + /run/snapd/lock/*.lock rwk, + + # Allow reading stored mount namespaces, + /run/snapd/ns/ r, + /run/snapd/ns/*.mnt r, + + # Allow reading per-snap desired mount profiles. Those are written by + # snapd and represent the desired layout and content connections. + /var/lib/snapd/mount/snap.*.fstab r, + + # Allow reading and writing actual per-snap mount profiles. Note that + # the second rule is generic to allow our tmpfile-rename approach to + # writing them. Those are written by snap-update-ns and represent the + # actual layout at a given moment. + /run/snapd/ns/*.fstab rw, + /run/snapd/ns/*.fstab.* rw, + + # NOTE: at this stage the /snap directory is stable as we have called + # pivot_root already. + + # Needed to perform mount/unmounts. + capability sys_admin, + + # Support mount profiles via the content interface. This should correspond + # to permutations of $SNAP -> $SNAP for reading and $SNAP_{DATA,COMMON} -> + # $SNAP_{DATA,COMMON} for both reading and writing. + # + # Note that: + # /snap/*/*/** + # is meant to mean: + # /snap/$SNAP_NAME/$SNAP_REVISION/and-any-subdirectory + # but: + # /var/snap/*/** + # is meant to mean: + # /var/snap/$SNAP_NAME/$SNAP_REVISION/ + mount options=(ro bind) /snap/*/** -> /snap/*/*/**, + mount options=(ro bind) /snap/*/** -> /var/snap/*/**, + mount options=(rw bind) /var/snap/*/** -> /var/snap/*/**, + mount options=(ro bind) /var/snap/*/** -> /var/snap/*/**, + + # Allow the content interface to bind fonts from the host filesystem + mount options=(ro bind) /var/lib/snapd/hostfs/usr/share/fonts/ -> /snap/*/*/**, + # Allow the desktop interface to bind fonts from the host filesystem + mount options=(ro bind) /var/lib/snapd/hostfs/usr/share/fonts/ -> /usr/share/fonts/, + mount options=(ro bind) /var/lib/snapd/hostfs/usr/local/share/fonts/ -> /usr/local/share/fonts/, + mount options=(ro bind) /var/lib/snapd/hostfs/var/cache/fontconfig/ -> /var/cache/fontconfig/, + + # Allow unmounts matching possible mounts listed above. + umount /snap/*/*/**, + umount /var/snap/*/**, + umount /usr/share/fonts, + umount /usr/local/share/fonts, + umount /var/cache/fontconfig, + + # But we don't want anyone to touch /snap/bin + audit deny mount /snap/bin/** -> /**, + audit deny mount /** -> /snap/bin/**, + + # Allow the content interface to bind fonts from the host filesystem + mount options=(ro bind) /var/lib/snapd/hostfs/usr/share/fonts/ -> /snap/*/*/**, + } } diff -Nru snapd-2.28.5/cmd/snap-confine/snap-confine-args.h snapd-2.29.3/cmd/snap-confine/snap-confine-args.h --- snapd-2.28.5/cmd/snap-confine/snap-confine-args.h 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/snap-confine-args.h 2017-10-23 06:17:27.000000000 +0000 @@ -77,7 +77,7 @@ * Cleanup an error with sc_args_free() * * This function is designed to be used with - * __attribute__((cleanup(sc_cleanup_args))). + * SC_CLEANUP(sc_cleanup_args). **/ void sc_cleanup_args(struct sc_args **ptr); diff -Nru snapd-2.28.5/cmd/snap-confine/snap-confine-args-test.c snapd-2.29.3/cmd/snap-confine/snap-confine-args-test.c --- snapd-2.28.5/cmd/snap-confine/snap-confine-args-test.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/snap-confine-args-test.c 2017-10-23 06:17:27.000000000 +0000 @@ -17,6 +17,7 @@ #include "snap-confine-args.h" #include "snap-confine-args.c" +#include "../libsnap-confine-private/cleanup-funcs.h" #include @@ -76,8 +77,8 @@ static void test_sc_nonfatal_parse_args__typical() { // Test that typical invocation of snap-confine is parsed correctly. - struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL; - struct sc_args *args __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; int argc; char **argv; @@ -109,7 +110,7 @@ static void test_sc_cleanup_args() { // Check that NULL argument parser can be cleaned up - struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; struct sc_args *args = NULL; sc_cleanup_args(&args); @@ -130,8 +131,8 @@ static void test_sc_nonfatal_parse_args__typical_classic() { // Test that typical invocation of snap-confine is parsed correctly. - struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL; - struct sc_args *args __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; int argc; char **argv; @@ -165,8 +166,8 @@ // Test that typical legacy invocation of snap-confine via the // ubuntu-core-launcher symlink, with duplicated security tag, is parsed // correctly. - struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL; - struct sc_args *args __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; int argc; char **argv; @@ -198,8 +199,8 @@ static void test_sc_nonfatal_parse_args__version() { // Test that snap-confine --version is detected. - struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL; - struct sc_args *args __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; int argc; char **argv; @@ -228,8 +229,8 @@ static void test_sc_nonfatal_parse_args__evil_input() { // Check that calling without any arguments is reported as error. - struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL; - struct sc_args *args __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; // NULL argcp/argvp attack args = sc_nonfatal_parse_args(NULL, NULL, &err); @@ -268,8 +269,8 @@ static void test_sc_nonfatal_parse_args__nothing_to_parse() { // Check that calling without any arguments is reported as error. - struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL; - struct sc_args *args __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; int argc; char **argv; @@ -287,8 +288,8 @@ static void test_sc_nonfatal_parse_args__no_security_tag() { // Check that lack of security tag is reported as error. - struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL; - struct sc_args *args __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; int argc; char **argv; @@ -309,8 +310,8 @@ static void test_sc_nonfatal_parse_args__no_executable() { // Check that lack of security tag is reported as error. - struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL; - struct sc_args *args __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; int argc; char **argv; @@ -331,8 +332,8 @@ static void test_sc_nonfatal_parse_args__unknown_option() { // Check that unrecognized option switch is reported as error. - struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL; - struct sc_args *args __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; int argc; char **argv; @@ -360,8 +361,7 @@ "--frozbonicator", NULL); // Call sc_nonfatal_parse_args() without an error handle - struct sc_args *args - __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; args = sc_nonfatal_parse_args(&argc, &argv, NULL); (void)args; @@ -379,8 +379,8 @@ static void test_sc_nonfatal_parse_args__base_snap() { // Check that --base specifies the name of the base snap. - struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL; - struct sc_args *args __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; int argc; char **argv; @@ -407,8 +407,8 @@ static void test_sc_nonfatal_parse_args__base_snap__missing_arg() { // Check that --base specifies the name of the base snap. - struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL; - struct sc_args *args __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; int argc; char **argv; @@ -429,8 +429,8 @@ static void test_sc_nonfatal_parse_args__base_snap__twice() { // Check that --base specifies the name of the base snap. - struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL; - struct sc_args *args __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; int argc; char **argv; diff -Nru snapd-2.28.5/cmd/snap-confine/snap-confine.c snapd-2.29.3/cmd/snap-confine/snap-confine.c --- snapd-2.28.5/cmd/snap-confine/snap-confine.c 2017-10-13 21:25:17.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/snap-confine.c 2017-10-23 06:17:27.000000000 +0000 @@ -26,6 +26,7 @@ #include #include +#include "../libsnap-confine-private/cgroup-freezer-support.h" #include "../libsnap-confine-private/classic.h" #include "../libsnap-confine-private/cleanup-funcs.h" #include "../libsnap-confine-private/locking.h" @@ -69,7 +70,7 @@ // https://forum.snapcraft.io/t/weird-udev-enumerate-error/2360/17 void sc_maybe_fixup_udev() { - glob_t glob_res __attribute__ ((cleanup(globfree))) = { + glob_t glob_res SC_CLEANUP(globfree) = { .gl_pathv = NULL,.gl_pathc = 0,.gl_offs = 0,}; const char *glob_pattern = "/run/udev/tags/snap_*/*nvidia*"; int err = glob(glob_pattern, 0, NULL, &glob_res); @@ -95,7 +96,7 @@ { // Use our super-defensive parser to figure out what we've been asked to do. struct sc_error *err = NULL; - struct sc_args *args __attribute__ ((cleanup(sc_cleanup_args))) = NULL; + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; args = sc_nonfatal_parse_args(&argc, &argv, &err); sc_die_on_error(err); @@ -141,11 +142,10 @@ } #endif - char *snap_context __attribute__ ((cleanup(sc_cleanup_string))) = NULL; + char *snap_context SC_CLEANUP(sc_cleanup_string) = NULL; // Do no get snap context value if running a hook (we don't want to overwrite hook's SNAP_COOKIE) if (!sc_is_hook_security_tag(security_tag)) { - struct sc_error *err - __attribute__ ((cleanup(sc_cleanup_error))) = NULL; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; snap_context = sc_cookie_get_from_snapd(snap_name, &err); if (err != NULL) { error("%s\n", sc_error_msg(err)); @@ -220,6 +220,12 @@ // old version sc_maybe_fixup_permissions(); sc_maybe_fixup_udev(); + + // Associate each snap process with a dedicated snap freezer + // control group. This simplifies testing if any processes + // belonging to a given snap are still alive. + // See the documentation of the function for details. + sc_cgroup_freezer_join(snap_name, getpid()); sc_unlock(snap_name, snap_lock_fd); // Reset path as we cannot rely on the path from the host OS to diff -Nru snapd-2.28.5/cmd/snap-confine/snap-confine.rst snapd-2.29.3/cmd/snap-confine/snap-confine.rst --- snapd-2.28.5/cmd/snap-confine/snap-confine.rst 2017-08-18 13:48:10.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/snap-confine.rst 2017-10-23 06:17:27.000000000 +0000 @@ -7,27 +7,37 @@ ----------------------------------------------- :Author: zygmunt.krynicki@canonical.com -:Date: 2016-10-05 +:Date: 2017-09-18 :Copyright: Canonical Ltd. -:Version: 1.0.43 -:Manual section: 5 +:Version: 2.28 +:Manual section: 1 :Manual group: snappy SYNOPSIS ======== - snap-confine SECURITY_TAG COMMAND [...ARGUMENTS] + snap-confine [--classic] [--base BASE] SECURITY_TAG COMMAND [...ARGUMENTS] DESCRIPTION =========== -The `snap-confine` is a program used internally by `snapd` to construct a -confined execution environment for snap applications. +The `snap-confine` is a program used internally by `snapd` to construct the +execution environment for snap applications. OPTIONS ======= -The `snap-confine` program does not support any options. +The `snap-confine` program accepts two options: + + `--classic` requests the so-called _classic_ _confinement_ in which + applications are not confined at all (like in classic systems, hence the + name). This disables the use of a dedicated, per-snap mount namespace. The + `snapd` service generates permissive apparmor and seccomp profiles that + allow everything. + + `--base BASE` directs snap-confine to use the given base snap as the root + filesystem. If omitted it defaults to the `core` snap. This is derived from + snap meta-data by `snapd` when starting the application process. FEATURES ======== @@ -38,11 +48,12 @@ `snap-confine` switches to the apparmor profile `$SECURITY_TAG`. The profile is **mandatory** and `snap-confine` will refuse to run without it. -has to be loaded into the kernel prior to using `snap-confine`. Typically this -is arranged for by `snapd`. The profile contains rich description of what the -application process is allowed to do, this includes system calls, file paths, -access patterns, linux capabilities, etc. The apparmor profile can also do -extensive dbus mediation. Refer to apparmor documentation for more details. +The profile has to be loaded into the kernel prior to using `snap-confine`. +Typically this is arranged for by `snapd`. The profile contains rich +description of what the application process is allowed to do, this includes +system calls, file paths, access patterns, linux capabilities, etc. The +apparmor profile can also do extensive dbus mediation. Refer to apparmor +documentation for more details. Seccomp profiles ---------------- @@ -53,11 +64,10 @@ file contains the seccomp bpf binary program that is loaded into the kernel by snap-confine. -The file is generated with the /usr/lib/snapd/snap-seccomp compiler -from the `$SECURITY_TAG.src` file that uses a custom syntax that -describes the set of allowed system calls and optionally their -arguments. The profile is then used to confine the started -application. +The file is generated with the `/usr/lib/snapd/snap-seccomp` compiler from the +`$SECURITY_TAG.src` file that uses a custom syntax that describes the set of +allowed system calls and optionally their arguments. The profile is then used +to confine the started application. As a security precaution disallowed system calls cause the started application executable to be killed by the kernel. In the future this restriction may be @@ -66,15 +76,20 @@ Mount profiles -------------- -`snap-confine` looks for the `/var/lib/snapd/mount/$SECURITY_TAG.fstab` file. -If present it is read, parsed and treated like a typical `fstab(5)` file. -The mount directives listed there are executed in order. All directives must -succeed as any failure will abort execution. +`snap-confine` uses a helper process, `snap-update-ns`, to apply the mount +namespace profile to freshly constructed mount namespace. That tool looks for +the `/var/lib/snapd/mount/snap.$SNAP_NAME.fstab` file. If present it is read, +parsed and treated like a mostly-typical `fstab(5)` file. The mount directives +listed there are executed in order. All directives must succeed as any failure +will abort execution. By default all mount entries start with the following flags: `bind`, `ro`, `nodev`, `nosuid`. Some of those flags can be reversed by an appropriate option (e.g. `rw` can cause the mount point to be writable). +Certain additional features are enabled and conveyed through the use of mount +options prefixed with `x-snapd-`. + As a security precaution only `bind` mounts are supported at this time. Quirks @@ -85,7 +100,7 @@ useful and important) have grown to rely on. This section documents the list of quirks: -- The /var/lib/lxd directory, if it exists on the host, is made available in +- The `/var/lib/lxd` directory, if it exists on the host, is made available in the execution environment. This allows various snaps, while running in devmode, to access the LXD socket. LP: #1613845 @@ -127,9 +142,9 @@ FILES ===== -`snap-confine` uses the following files: +`snap-confine` and `snap-update-ns` use the following files: -`/var/lib/snapd/mount/*.fstab`: +`/var/lib/snapd/mount/snap.*.fstab`: Description of the mount profile. diff -Nru snapd-2.28.5/cmd/snap-confine/udev-support.c snapd-2.29.3/cmd/snap-confine/udev-support.c --- snapd-2.28.5/cmd/snap-confine/udev-support.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/snap-confine/udev-support.c 2017-11-09 09:07:17.000000000 +0000 @@ -50,12 +50,17 @@ if (real_uid != 0 && effective_uid == 0) if (setuid(0) != 0) die("setuid failed"); - char buf[64]; + char buf[64] = { 0 }; // pass snappy-add-dev an empty environment so the // user-controlled environment can't be used to subvert // snappy-add-dev char *env[] = { NULL }; - sc_must_snprintf(buf, sizeof(buf), "%u:%u", major, minor); + if (minor == UINT_MAX) { + sc_must_snprintf(buf, sizeof(buf), "%u:*", major); + } else { + sc_must_snprintf(buf, sizeof(buf), "%u:%u", major, + minor); + } debug("running snappy-app-dev add %s %s %s", udev_s->tagname, path, buf); execle("/lib/udev/snappy-app-dev", "/lib/udev/snappy-app-dev", @@ -177,7 +182,7 @@ die("snappy_udev->tagname has invalid length"); // create devices cgroup controller - char cgroup_dir[PATH_MAX]; + char cgroup_dir[PATH_MAX] = { 0 }; sc_must_snprintf(cgroup_dir, sizeof(cgroup_dir), "/sys/fs/cgroup/devices/%s/", security_tag); @@ -186,11 +191,11 @@ die("mkdir failed"); // move ourselves into it - char cgroup_file[PATH_MAX]; + char cgroup_file[PATH_MAX] = { 0 }; sc_must_snprintf(cgroup_file, sizeof(cgroup_file), "%s%s", cgroup_dir, "tasks"); - char buf[128]; + char buf[128] = { 0 }; sc_must_snprintf(buf, sizeof(buf), "%i", getpid()); write_string_to_file(cgroup_file, buf); @@ -206,6 +211,19 @@ for (int i = 0; static_devices[i] != NULL; i++) run_snappy_app_dev_add(udev_s, static_devices[i]); + // add glob for current and future PTY slaves. We unconditionally add + // them since we use a devpts newinstance. Unix98 PTY slaves major + // are 136-143. + // https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/devices.txt + for (unsigned pty_major = 136; pty_major <= 143; pty_major++) { + // '/dev/pts/slaves' is only used for debugging and by + // /lib/udev/snappy-app-dev to determine if it is a block + // device, so just use something to indicate what the + // addition is for + _run_snappy_app_dev_add_majmin(udev_s, "/dev/pts/slaves", + pty_major, UINT_MAX); + } + // nvidia modules are proprietary and therefore aren't in sysfs and // can't be udev tagged. For now, just add existing nvidia devices to // the cgroup unconditionally (AppArmor will still mediate the access). @@ -256,6 +274,13 @@ MAJOR(sbuf.st_rdev), MINOR(sbuf.st_rdev)); } + // /dev/uhid isn't represented in sysfs, so add it to the device cgroup + // if it exists and let AppArmor handle the mediation + if (stat("/dev/uhid", &sbuf) == 0) { + _run_snappy_app_dev_add_majmin(udev_s, "/dev/uhid", + MAJOR(sbuf.st_rdev), + MINOR(sbuf.st_rdev)); + } // add the assigned devices while (udev_s->assigned != NULL) { const char *path = udev_list_entry_get_name(udev_s->assigned); diff -Nru snapd-2.28.5/cmd/snap-discard-ns/snap-discard-ns.c snapd-2.29.3/cmd/snap-discard-ns/snap-discard-ns.c --- snapd-2.28.5/cmd/snap-discard-ns/snap-discard-ns.c 2017-10-13 20:09:56.000000000 +0000 +++ snapd-2.29.3/cmd/snap-discard-ns/snap-discard-ns.c 2017-10-23 06:17:27.000000000 +0000 @@ -39,7 +39,7 @@ sc_close_ns_group(group); } // Unlink the current mount profile, if any. - char profile_path[PATH_MAX]; + char profile_path[PATH_MAX] = { 0 }; sc_must_snprintf(profile_path, sizeof(profile_path), "/run/snapd/ns/snap.%s.fstab", snap_name); if (unlink(profile_path) < 0) { diff -Nru snapd-2.28.5/cmd/snap-exec/export_test.go snapd-2.29.3/cmd/snap-exec/export_test.go --- snapd-2.28.5/cmd/snap-exec/export_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-exec/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,51 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2014-2015 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 . + * + */ + +package main + +var ( + ExpandEnvCmdArgs = expandEnvCmdArgs + FindCommand = findCommand + ParseArgs = parseArgs + Run = run + ExecApp = execApp + ExecHook = execHook +) + +func MockSyscallExec(f func(argv0 string, argv []string, envv []string) (err error)) func() { + origSyscallExec := syscallExec + syscallExec = f + return func() { + syscallExec = origSyscallExec + } +} + +func SetOptsCommand(s string) { + opts.Command = s +} +func GetOptsCommand() string { + return opts.Command +} + +func SetOptsHook(s string) { + opts.Hook = s +} +func GetOptsHook() string { + return opts.Hook +} diff -Nru snapd-2.28.5/cmd/snap-exec/main.go snapd-2.29.3/cmd/snap-exec/main.go --- snapd-2.28.5/cmd/snap-exec/main.go 2017-09-20 07:05:01.000000000 +0000 +++ snapd-2.29.3/cmd/snap-exec/main.go 2017-10-23 06:17:27.000000000 +0000 @@ -31,6 +31,7 @@ "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/snap" + "github.com/snapcore/snapd/snap/snapenv" ) // for the tests @@ -42,6 +43,11 @@ Hook string `long:"hook" description:"hook to run" hidden:"yes"` } +func init() { + // plug/slot sanitization not used nor possible from snap-exec, make it no-op + snap.SanitizePlugsSlots = func(snapInfo *snap.Info) {} +} + func main() { if err := run(); err != nil { fmt.Fprintf(os.Stderr, "cannot snap-exec: %s\n", err) @@ -83,10 +89,10 @@ // Now actually handle the dispatching if opts.Hook != "" { - return snapExecHook(snapApp, revision, opts.Hook) + return execHook(snapApp, revision, opts.Hook) } - return snapExecApp(snapApp, revision, opts.Command, extraArgs) + return execApp(snapApp, revision, opts.Command, extraArgs) } const defaultShell = "/bin/bash" @@ -118,7 +124,22 @@ return cmd, nil } -func snapExecApp(snapApp, revision, command string, args []string) error { +// expandEnvCmdArgs takes the string list of commandline arguments +// and expands any $VAR with the given var from the env argument. +func expandEnvCmdArgs(args []string, env map[string]string) []string { + cmdArgs := make([]string, 0, len(args)) + for _, arg := range args { + maybeExpanded := os.Expand(arg, func(k string) string { + return env[k] + }) + if maybeExpanded != "" { + cmdArgs = append(cmdArgs, maybeExpanded) + } + } + return cmdArgs +} + +func execApp(snapApp, revision, command string, args []string) error { rev, err := snap.ParseRevision(revision) if err != nil { return fmt.Errorf("cannot parse revision %q: %s", revision, err) @@ -141,15 +162,25 @@ if err != nil { return err } + + // build the environment from the yaml, translating TMPDIR and + // similar variables back from where they were hidden when + // invoking the setuid snap-confine. + env := []string{} + for _, kv := range os.Environ() { + if strings.HasPrefix(kv, snapenv.PreservedUnsafePrefix) { + kv = kv[len(snapenv.PreservedUnsafePrefix):] + } + env = append(env, kv) + } + env = append(env, osutil.SubstituteEnv(app.Env())...) + // strings.Split() is ok here because we validate all app fields // and the whitelist is pretty strict (see // snap/validate.go:appContentWhitelist) - cmdArgv := strings.Split(cmdAndArgs, " ") - cmd := cmdArgv[0] - cmdArgs := cmdArgv[1:] - - // build the environment from the yaml - env := append(os.Environ(), osutil.SubstituteEnv(app.Env())...) + tmpArgv := strings.Split(cmdAndArgs, " ") + cmd := tmpArgv[0] + cmdArgs := expandEnvCmdArgs(tmpArgv[1:], osutil.EnvMap(env)) // run the command fullCmd := filepath.Join(app.Snap.MountDir(), cmd) @@ -174,7 +205,7 @@ return nil } -func snapExecHook(snapName, revision, hookName string) error { +func execHook(snapName, revision, hookName string) error { rev, err := snap.ParseRevision(revision) if err != nil { return err diff -Nru snapd-2.28.5/cmd/snap-exec/main_test.go snapd-2.29.3/cmd/snap-exec/main_test.go --- snapd-2.28.5/cmd/snap-exec/main_test.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/cmd/snap-exec/main_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -17,7 +17,7 @@ * */ -package main +package main_test import ( "fmt" @@ -25,7 +25,6 @@ "os" "os/exec" "path/filepath" - "syscall" "testing" . "gopkg.in/check.v1" @@ -34,6 +33,8 @@ "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/snap/snaptest" "github.com/snapcore/snapd/testutil" + + snapExec "github.com/snapcore/snapd/cmd/snap-exec" ) // Hook up check.v1 into the "go test" runner @@ -45,12 +46,11 @@ func (s *snapExecSuite) SetUpTest(c *C) { // clean previous parse runs - opts.Command = "" - opts.Hook = "" + snapExec.SetOptsCommand("") + snapExec.SetOptsHook("") } func (s *snapExecSuite) TearDown(c *C) { - syscallExec = syscall.Exec dirs.SetRootDir("/") } @@ -58,7 +58,7 @@ version: 1.0 apps: app: - command: run-app cmd-arg1 + command: run-app cmd-arg1 $SNAP_DATA stop-command: stop-app post-stop-command: post-stop-app environment: @@ -86,13 +86,13 @@ func (s *snapExecSuite) TestInvalidCombinedParameters(c *C) { invalidParameters := []string{"--hook=hook-name", "--command=command-name", "snap-name"} - _, _, err := parseArgs(invalidParameters) + _, _, err := snapExec.ParseArgs(invalidParameters) c.Check(err, ErrorMatches, ".*cannot use --hook and --command together.*") } func (s *snapExecSuite) TestInvalidExtraParameters(c *C) { invalidParameters := []string{"--hook=hook-name", "snap-name", "foo", "bar"} - _, _, err := parseArgs(invalidParameters) + _, _, err := snapExec.ParseArgs(invalidParameters) c.Check(err, ErrorMatches, ".*too many arguments for hook \"hook-name\": snap-name foo bar.*") } @@ -104,11 +104,11 @@ cmd string expected string }{ - {cmd: "", expected: `run-app cmd-arg1`}, + {cmd: "", expected: `run-app cmd-arg1 $SNAP_DATA`}, {cmd: "stop", expected: "stop-app"}, {cmd: "post-stop", expected: "post-stop-app"}, } { - cmd, err := findCommand(info.Apps["app"], t.cmd) + cmd, err := snapExec.FindCommand(info.Apps["app"], t.cmd) c.Check(err, IsNil) c.Check(cmd, Equals, t.expected) } @@ -118,7 +118,7 @@ info, err := snap.InfoFromSnapYaml(mockYaml) c.Assert(err, IsNil) - _, err = findCommand(info.Apps["app"], "xxx") + _, err = snapExec.FindCommand(info.Apps["app"], "xxx") c.Check(err, ErrorMatches, `cannot use "xxx" command`) } @@ -126,7 +126,7 @@ info, err := snap.InfoFromSnapYaml(mockYaml) c.Assert(err, IsNil) - _, err = findCommand(info.Apps["nostop"], "stop") + _, err = snapExec.FindCommand(info.Apps["nostop"], "stop") c.Check(err, ErrorMatches, `no "stop" command found for "nostop"`) } @@ -139,15 +139,16 @@ execArgv0 := "" execArgs := []string{} execEnv := []string{} - syscallExec = func(argv0 string, argv []string, env []string) error { + restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { execArgv0 = argv0 execArgs = argv execEnv = env return nil - } + }) + defer restore() // launch and verify its run the right way - err := snapExecApp("snapname.app", "42", "stop", []string{"arg1", "arg2"}) + err := snapExec.ExecApp("snapname.app", "42", "stop", []string{"arg1", "arg2"}) c.Assert(err, IsNil) c.Check(execArgv0, Equals, fmt.Sprintf("%s/snapname/42/stop-app", dirs.SnapMountDir)) c.Check(execArgs, DeepEquals, []string{execArgv0, "arg1", "arg2"}) @@ -164,14 +165,15 @@ execArgv0 := "" execArgs := []string{} - syscallExec = func(argv0 string, argv []string, env []string) error { + restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { execArgv0 = argv0 execArgs = argv return nil - } + }) + defer restore() // launch and verify it ran correctly - err := snapExecHook("snapname", "42", "configure") + err := snapExec.ExecHook("snapname", "42", "configure") c.Assert(err, IsNil) c.Check(execArgv0, Equals, fmt.Sprintf("%s/snapname/42/meta/hooks/configure", dirs.SnapMountDir)) c.Check(execArgs, DeepEquals, []string{execArgv0}) @@ -183,26 +185,26 @@ Revision: snap.R("42"), }) - err := snapExecHook("snapname", "42", "missing-hook") + err := snapExec.ExecHook("snapname", "42", "missing-hook") c.Assert(err, NotNil) c.Assert(err, ErrorMatches, "cannot find hook \"missing-hook\" in \"snapname\"") } func (s *snapExecSuite) TestSnapExecIgnoresUnknownArgs(c *C) { - snapApp, rest, err := parseArgs([]string{"--command=shell", "snapname.app", "--arg1", "arg2"}) + snapApp, rest, err := snapExec.ParseArgs([]string{"--command=shell", "snapname.app", "--arg1", "arg2"}) c.Assert(err, IsNil) - c.Assert(opts.Command, Equals, "shell") + c.Assert(snapExec.GetOptsCommand(), Equals, "shell") c.Assert(snapApp, DeepEquals, "snapname.app") c.Assert(rest, DeepEquals, []string{"--arg1", "arg2"}) } func (s *snapExecSuite) TestSnapExecErrorsOnUnknown(c *C) { - _, _, err := parseArgs([]string{"--command=shell", "--unknown", "snapname.app", "--arg1", "arg2"}) + _, _, err := snapExec.ParseArgs([]string{"--command=shell", "--unknown", "snapname.app", "--arg1", "arg2"}) c.Check(err, ErrorMatches, "unknown flag `unknown'") } func (s *snapExecSuite) TestSnapExecErrorsOnMissingSnapApp(c *C) { - _, _, err := parseArgs([]string{"--command=shell"}) + _, _, err := snapExec.ParseArgs([]string{"--command=shell"}) c.Check(err, ErrorMatches, "need the application to run as argument") } @@ -227,11 +229,12 @@ // we can not use the real syscall.execv here because it would // replace the entire test :) - syscallExec = actuallyExec + restore := snapExec.MockSyscallExec(actuallyExec) + defer restore() // run it os.Args = []string{"snap-exec", "snapname.app", "foo", "--bar=baz", "foobar"} - err = run() + err = snapExec.Run() c.Assert(err, IsNil) output, err := ioutil.ReadFile(canaryFile) @@ -268,11 +271,12 @@ // we can not use the real syscall.execv here because it would // replace the entire test :) - syscallExec = actuallyExec + restore := snapExec.MockSyscallExec(actuallyExec) + defer restore() // run it os.Args = []string{"snap-exec", "--hook=configure", "snapname"} - err := run() + err := snapExec.Run() c.Assert(err, IsNil) output, err := ioutil.ReadFile(canaryFile) @@ -299,17 +303,81 @@ execArgv0 := "" execArgs := []string{} execEnv := []string{} - syscallExec = func(argv0 string, argv []string, env []string) error { + restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { execArgv0 = argv0 execArgs = argv execEnv = env return nil - } + }) + defer restore() // launch and verify its run the right way - err := snapExecApp("snapname.app", "42", "shell", []string{"-c", "echo foo"}) + err := snapExec.ExecApp("snapname.app", "42", "shell", []string{"-c", "echo foo"}) c.Assert(err, IsNil) c.Check(execArgv0, Equals, "/bin/bash") c.Check(execArgs, DeepEquals, []string{execArgv0, "-c", "echo foo"}) c.Check(execEnv, testutil.Contains, "LD_LIBRARY_PATH=/some/path/lib") } + +func (s *snapExecSuite) TestSnapExecAppIntegrationWithVars(c *C) { + dirs.SetRootDir(c.MkDir()) + snaptest.MockSnap(c, string(mockYaml), string(mockContents), &snap.SideInfo{ + Revision: snap.R("42"), + }) + + execArgv0 := "" + execArgs := []string{} + execEnv := []string{} + restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { + execArgv0 = argv0 + execArgs = argv + execEnv = env + return nil + }) + defer restore() + + // setup env + os.Setenv("SNAP_DATA", "/var/snap/snapname/42") + defer os.Unsetenv("SNAP_DATA") + + // launch and verify its run the right way + err := snapExec.ExecApp("snapname.app", "42", "", []string{"user-arg1"}) + c.Assert(err, IsNil) + c.Check(execArgv0, Equals, fmt.Sprintf("%s/snapname/42/run-app", dirs.SnapMountDir)) + c.Check(execArgs, DeepEquals, []string{execArgv0, "cmd-arg1", "/var/snap/snapname/42", "user-arg1"}) + c.Check(execEnv, testutil.Contains, "BASE_PATH=/some/path") + c.Check(execEnv, testutil.Contains, "LD_LIBRARY_PATH=/some/path/lib") + c.Check(execEnv, testutil.Contains, fmt.Sprintf("MY_PATH=%s", os.Getenv("PATH"))) +} + +func (s *snapExecSuite) TestSnapExecExpandEnvCmdArgs(c *C) { + for _, t := range []struct { + args []string + env map[string]string + expected []string + }{ + { + args: []string{"foo"}, + env: nil, + expected: []string{"foo"}, + }, + { + args: []string{"$var"}, + env: map[string]string{"var": "value"}, + expected: []string{"value"}, + }, + { + args: []string{"foo", "$not_existing"}, + env: nil, + expected: []string{"foo"}, + }, + { + args: []string{"foo", "$var", "baz"}, + env: map[string]string{"var": "bar", "unrelated": "env"}, + expected: []string{"foo", "bar", "baz"}, + }, + } { + c.Check(snapExec.ExpandEnvCmdArgs(t.args, t.env), DeepEquals, t.expected) + + } +} diff -Nru snapd-2.28.5/cmd/snap-repair/cmd_done_retry_skip.go snapd-2.29.3/cmd/snap-repair/cmd_done_retry_skip.go --- snapd-2.28.5/cmd/snap-repair/cmd_done_retry_skip.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/cmd_done_retry_skip.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,82 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main + +import ( + "fmt" + "os" + "strconv" +) + +func init() { + cmd, err := parser.AddCommand("done", "Signal repair is done", "", &cmdDone{}) + if err != nil { + + panic(err) + } + cmd.Hidden = true + + cmd, err = parser.AddCommand("skip", "Signal repair should be skipped", "", &cmdSkip{}) + if err != nil { + panic(err) + } + cmd.Hidden = true + + cmd, err = parser.AddCommand("retry", "Signal repair must be retried next time", "", &cmdRetry{}) + if err != nil { + panic(err) + } + cmd.Hidden = true +} + +func writeToStatusFD(msg string) error { + statusFdStr := os.Getenv("SNAP_REPAIR_STATUS_FD") + if statusFdStr == "" { + return fmt.Errorf("cannot find SNAP_REPAIR_STATUS_FD environment") + } + fd, err := strconv.Atoi(statusFdStr) + if err != nil { + return fmt.Errorf("cannot parse SNAP_REPAIR_STATUS_FD environment: %s", err) + } + f := os.NewFile(uintptr(fd), "") + defer f.Close() + if _, err := f.Write([]byte(msg + "\n")); err != nil { + return err + } + return nil +} + +type cmdDone struct{} + +func (c *cmdDone) Execute(args []string) error { + return writeToStatusFD("done") +} + +type cmdSkip struct{} + +func (c *cmdSkip) Execute([]string) error { + return writeToStatusFD("skip") +} + +type cmdRetry struct{} + +func (c *cmdRetry) Execute([]string) error { + return writeToStatusFD("retry") +} diff -Nru snapd-2.28.5/cmd/snap-repair/cmd_done_retry_skip_test.go snapd-2.29.3/cmd/snap-repair/cmd_done_retry_skip_test.go --- snapd-2.28.5/cmd/snap-repair/cmd_done_retry_skip_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/cmd_done_retry_skip_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,81 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main_test + +import ( + "io/ioutil" + "os" + "strconv" + "syscall" + + . "gopkg.in/check.v1" + + repair "github.com/snapcore/snapd/cmd/snap-repair" +) + +func (r *repairSuite) TestStatusNoStatusFdEnv(c *C) { + for _, s := range []string{"done", "skip", "retry"} { + err := repair.ParseArgs([]string{s}) + c.Check(err, ErrorMatches, "cannot find SNAP_REPAIR_STATUS_FD environment") + } +} + +func (r *repairSuite) TestStatusBadStatusFD(c *C) { + for _, s := range []string{"done", "skip", "retry"} { + os.Setenv("SNAP_REPAIR_STATUS_FD", "123456789") + defer os.Unsetenv("SNAP_REPAIR_STATUS_FD") + + err := repair.ParseArgs([]string{s}) + c.Check(err, ErrorMatches, `write : bad file descriptor`) + } +} + +func (r *repairSuite) TestStatusUnparsableStatusFD(c *C) { + for _, s := range []string{"done", "skip", "retry"} { + os.Setenv("SNAP_REPAIR_STATUS_FD", "xxx") + defer os.Unsetenv("SNAP_REPAIR_STATUS_FD") + + err := repair.ParseArgs([]string{s}) + c.Check(err, ErrorMatches, `cannot parse SNAP_REPAIR_STATUS_FD environment: strconv.*: parsing "xxx": invalid syntax`) + } +} + +func (r *repairSuite) TestStatusHappy(c *C) { + for _, s := range []string{"done", "skip", "retry"} { + rp, wp, err := os.Pipe() + c.Assert(err, IsNil) + defer rp.Close() + defer wp.Close() + + fd, e := syscall.Dup(int(wp.Fd())) + c.Assert(e, IsNil) + wp.Close() + + os.Setenv("SNAP_REPAIR_STATUS_FD", strconv.Itoa(fd)) + defer os.Unsetenv("SNAP_REPAIR_STATUS_FD") + + err = repair.ParseArgs([]string{s}) + c.Check(err, IsNil) + + status, err := ioutil.ReadAll(rp) + c.Assert(err, IsNil) + c.Check(string(status), Equals, s+"\n") + } +} diff -Nru snapd-2.28.5/cmd/snap-repair/cmd_list.go snapd-2.29.3/cmd/snap-repair/cmd_list.go --- snapd-2.28.5/cmd/snap-repair/cmd_list.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/cmd_list.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,74 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main + +import ( + "fmt" + "text/tabwriter" +) + +func init() { + const ( + short = "Lists repairs run on this device" + long = "" + ) + + if _, err := parser.AddCommand("list", short, long, &cmdList{}); err != nil { + panic(err) + } + +} + +type cmdList struct{} + +func (c *cmdList) Execute([]string) error { + w := tabwriter.NewWriter(Stdout, 5, 3, 2, ' ', 0) + defer w.Flush() + + // FIXME: this will not currently list the repairs that are + // skipped because of e.g. wrong architecture + + // directory structure is: + // var/lib/snapd/run/repairs/ + // canonical/ + // 1/ + // r0.retry + // r0.script + // r1.done + // r1.script + // 2/ + // r3.done + // r3.script + repairTraces, err := newRepairTraces("*", "*") + if err != nil { + return err + } + if len(repairTraces) == 0 { + fmt.Fprintf(Stderr, "no repairs yet\n") + return nil + } + + fmt.Fprintf(w, "Repair\tRev\tStatus\tSummary\n") + for _, t := range repairTraces { + fmt.Fprintf(w, "%s\t%v\t%s\t%s\n", t.Repair(), t.Revision(), t.Status(), t.Summary()) + } + + return nil +} diff -Nru snapd-2.28.5/cmd/snap-repair/cmd_list_test.go snapd-2.29.3/cmd/snap-repair/cmd_list_test.go --- snapd-2.28.5/cmd/snap-repair/cmd_list_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/cmd_list_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,47 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main_test + +import ( + . "gopkg.in/check.v1" + + repair "github.com/snapcore/snapd/cmd/snap-repair" +) + +func (r *repairSuite) TestListNoRepairsYet(c *C) { + err := repair.ParseArgs([]string{"list"}) + c.Check(err, IsNil) + c.Check(r.Stdout(), Equals, "") + c.Check(r.Stderr(), Equals, "no repairs yet\n") +} + +func (r *repairSuite) TestListRepairsSimple(c *C) { + makeMockRepairState(c) + + err := repair.ParseArgs([]string{"list"}) + c.Check(err, IsNil) + c.Check(r.Stdout(), Equals, `Repair Rev Status Summary +canonical-1 3 retry repair one +my-brand-1 1 done my-brand repair one +my-brand-2 2 skip my-brand repair two +my-brand-3 0 running my-brand repair three +`) + c.Check(r.Stderr(), Equals, "") +} diff -Nru snapd-2.28.5/cmd/snap-repair/cmd_run.go snapd-2.29.3/cmd/snap-repair/cmd_run.go --- snapd-2.28.5/cmd/snap-repair/cmd_run.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/cmd_run.go 2017-10-23 06:17:27.000000000 +0000 @@ -21,6 +21,12 @@ import ( "fmt" + "net/url" + "os" + "path/filepath" + + "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/osutil" ) func init() { @@ -37,7 +43,60 @@ type cmdRun struct{} +var baseURL *url.URL + +func init() { + var baseurl string + if osutil.GetenvBool("SNAPPY_USE_STAGING_STORE") { + baseurl = "https://api.staging.snapcraft.io/v2/" + } else { + baseurl = "https://api.snapcraft.io/v2/" + } + + var err error + baseURL, err = url.Parse(baseurl) + if err != nil { + panic(fmt.Sprintf("cannot setup base url: %v", err)) + } +} + func (c *cmdRun) Execute(args []string) error { - fmt.Fprintf(Stdout, "run is not implemented yet\n") + if err := os.MkdirAll(dirs.SnapRunRepairDir, 0755); err != nil { + return err + } + flock, err := osutil.NewFileLock(filepath.Join(dirs.SnapRunRepairDir, "lock")) + if err != nil { + return err + } + err = flock.TryLock() + if err == osutil.ErrAlreadyLocked { + return fmt.Errorf("cannot run, another snap-repair run already executing") + } + if err != nil { + return err + } + defer flock.Unlock() + + run := NewRunner() + run.BaseURL = baseURL + err = run.LoadState() + if err != nil { + return err + } + + for { + repair, err := run.Next("canonical") + if err == ErrRepairNotFound { + // no more repairs + break + } + if err != nil { + return err + } + + if err := repair.Run(); err != nil { + return err + } + } return nil } diff -Nru snapd-2.28.5/cmd/snap-repair/cmd_run_test.go snapd-2.29.3/cmd/snap-repair/cmd_run_test.go --- snapd-2.28.5/cmd/snap-repair/cmd_run_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/cmd_run_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,14 +20,53 @@ package main_test import ( + "os" + "path/filepath" + . "gopkg.in/check.v1" + "github.com/snapcore/snapd/asserts" + "github.com/snapcore/snapd/asserts/sysdb" repair "github.com/snapcore/snapd/cmd/snap-repair" + "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/osutil" ) func (r *repairSuite) TestRun(c *C) { + r1 := sysdb.InjectTrusted(r.storeSigning.Trusted) + defer r1() + r2 := repair.MockTrustedRepairRootKeys([]*asserts.AccountKey{r.repairRootAcctKey}) + defer r2() + + r.freshState(c) + + const script = `#!/bin/sh +echo "happy output" +echo "done" >&$SNAP_REPAIR_STATUS_FD +exit 0 +` + seqRepairs := r.signSeqRepairs(c, []string{makeMockRepair(script)}) + mockServer := makeMockServer(c, &seqRepairs, false) + defer mockServer.Close() + + repair.MockBaseURL(mockServer.URL) + err := repair.ParseArgs([]string{"run"}) c.Check(err, IsNil) - c.Check(r.Stdout(), Equals, "run is not implemented yet\n") + c.Check(r.Stdout(), HasLen, 0) + + c.Check(osutil.FileExists(filepath.Join(dirs.SnapRepairRunDir, "canonical", "1", "r0.done")), Equals, true) +} + +func (r *repairSuite) TestRunAlreadyLocked(c *C) { + err := os.MkdirAll(dirs.SnapRunRepairDir, 0700) + c.Assert(err, IsNil) + flock, err := osutil.NewFileLock(filepath.Join(dirs.SnapRunRepairDir, "lock")) + c.Assert(err, IsNil) + err = flock.Lock() + c.Assert(err, IsNil) + defer flock.Unlock() + err = repair.ParseArgs([]string{"run"}) + c.Check(err, ErrorMatches, `cannot run, another snap-repair run already executing`) } diff -Nru snapd-2.28.5/cmd/snap-repair/cmd_show.go snapd-2.29.3/cmd/snap-repair/cmd_show.go --- snapd-2.28.5/cmd/snap-repair/cmd_show.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/cmd_show.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,91 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main + +import ( + "fmt" + "io" + "strings" +) + +func init() { + const ( + short = "Shows specific repairs run on this device" + long = "" + ) + + if _, err := parser.AddCommand("show", short, long, &cmdShow{}); err != nil { + panic(err) + } + +} + +type cmdShow struct { + Positional struct { + Repair []string `positional-arg-name:""` + } `positional-args:"yes"` +} + +func showRepairDetails(w io.Writer, repair string) error { + i := strings.LastIndex(repair, "-") + if i < 0 { + return fmt.Errorf("cannot parse repair %q", repair) + } + brand := repair[:i] + seq := repair[i+1:] + + repairTraces, err := newRepairTraces(brand, seq) + if err != nil { + return err + } + if len(repairTraces) == 0 { + return fmt.Errorf("cannot find repair \"%s-%s\"", brand, seq) + } + + for _, trace := range repairTraces { + fmt.Fprintf(w, "repair: %s\n", trace.Repair()) + fmt.Fprintf(w, "revision: %s\n", trace.Revision()) + fmt.Fprintf(w, "status: %s\n", trace.Status()) + fmt.Fprintf(w, "summary: %s\n", trace.Summary()) + + fmt.Fprintf(w, "script:\n") + if err := trace.WriteScriptIndented(w, 2); err != nil { + fmt.Fprintf(w, "%serror: %s\n", indentPrefix(2), err) + } + + fmt.Fprintf(w, "output:\n") + if err := trace.WriteOutputIndented(w, 2); err != nil { + fmt.Fprintf(w, "%serror: %s\n", indentPrefix(2), err) + } + } + + return nil +} + +func (c *cmdShow) Execute([]string) error { + for _, repair := range c.Positional.Repair { + if err := showRepairDetails(Stdout, repair); err != nil { + return err + } + fmt.Fprintf(Stdout, "\n") + } + + return nil +} diff -Nru snapd-2.28.5/cmd/snap-repair/cmd_show_test.go snapd-2.29.3/cmd/snap-repair/cmd_show_test.go --- snapd-2.28.5/cmd/snap-repair/cmd_show_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/cmd_show_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,142 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main_test + +import ( + "fmt" + "os" + "path/filepath" + + . "gopkg.in/check.v1" + + repair "github.com/snapcore/snapd/cmd/snap-repair" + "github.com/snapcore/snapd/dirs" +) + +func (r *repairSuite) TestShowRepairSingle(c *C) { + makeMockRepairState(c) + + err := repair.ParseArgs([]string{"show", "canonical-1"}) + c.Check(err, IsNil) + c.Check(r.Stdout(), Equals, `repair: canonical-1 +revision: 3 +status: retry +summary: repair one +script: + #!/bin/sh + echo retry output +output: + retry output + +`) + +} + +func (r *repairSuite) TestShowRepairMultiple(c *C) { + makeMockRepairState(c) + + // repair.ParseArgs() always appends to its internal slice: + // cmdShow.Positional.Repair. To workaround this we create a + // new cmdShow here + err := repair.NewCmdShow("canonical-1", "my-brand-1", "my-brand-2").Execute(nil) + c.Check(err, IsNil) + c.Check(r.Stdout(), Equals, `repair: canonical-1 +revision: 3 +status: retry +summary: repair one +script: + #!/bin/sh + echo retry output +output: + retry output + +repair: my-brand-1 +revision: 1 +status: done +summary: my-brand repair one +script: + #!/bin/sh + echo done output +output: + done output + +repair: my-brand-2 +revision: 2 +status: skip +summary: my-brand repair two +script: + #!/bin/sh + echo skip output +output: + skip output + +`) +} + +func (r *repairSuite) TestShowRepairErrorNoRepairDir(c *C) { + dirs.SetRootDir(c.MkDir()) + + err := repair.NewCmdShow("canonical-1").Execute(nil) + c.Check(err, ErrorMatches, `cannot find repair "canonical-1"`) +} + +func (r *repairSuite) TestShowRepairSingleWithoutScript(c *C) { + makeMockRepairState(c) + scriptPath := filepath.Join(dirs.SnapRepairRunDir, "canonical/1", "r3.script") + err := os.Remove(scriptPath) + c.Assert(err, IsNil) + + err = repair.NewCmdShow("canonical-1").Execute(nil) + c.Check(err, IsNil) + c.Check(r.Stdout(), Equals, fmt.Sprintf(`repair: canonical-1 +revision: 3 +status: retry +summary: repair one +script: + error: open %s: no such file or directory +output: + retry output + +`, scriptPath)) + +} + +func (r *repairSuite) TestShowRepairSingleUnreadableOutput(c *C) { + makeMockRepairState(c) + scriptPath := filepath.Join(dirs.SnapRepairRunDir, "canonical/1", "r3.retry") + err := os.Chmod(scriptPath, 0000) + c.Assert(err, IsNil) + defer os.Chmod(scriptPath, 0644) + + err = repair.NewCmdShow("canonical-1").Execute(nil) + c.Check(err, IsNil) + c.Check(r.Stdout(), Equals, fmt.Sprintf(`repair: canonical-1 +revision: 3 +status: retry +summary: - +script: + #!/bin/sh + echo retry output +output: + error: open %s: permission denied + +`, scriptPath)) + +} diff -Nru snapd-2.28.5/cmd/snap-repair/export_test.go snapd-2.29.3/cmd/snap-repair/export_test.go --- snapd-2.28.5/cmd/snap-repair/export_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,6 +20,7 @@ package main import ( + "net/url" "time" "gopkg.in/retry.v1" @@ -34,6 +35,18 @@ Run = run ) +func MockBaseURL(baseurl string) (restore func()) { + orig := baseURL + u, err := url.Parse(baseurl) + if err != nil { + panic(err) + } + baseURL = u + return func() { + baseURL = orig + } +} + func MockFetchRetryStrategy(strategy retry.Strategy) (restore func()) { originalFetchRetryStrategy := fetchRetryStrategy fetchRetryStrategy = strategy @@ -66,6 +79,10 @@ } } +func TrustedRepairRootKeys() []*asserts.AccountKey { + return trustedRepairRootKeys +} + func (run *Runner) BrandModel() (brand, model string) { return run.state.Device.Brand, run.state.Device.Model } @@ -98,8 +115,28 @@ run.state.Sequences[brand] = sequence } +func MockDefaultRepairTimeout(d time.Duration) (restore func()) { + orig := defaultRepairTimeout + defaultRepairTimeout = d + return func() { + defaultRepairTimeout = orig + } +} + +func MockErrtrackerReportRepair(mock func(string, string, string, map[string]string) (string, error)) (restore func()) { + prev := errtrackerReportRepair + errtrackerReportRepair = mock + return func() { errtrackerReportRepair = prev } +} + func MockTimeNow(f func() time.Time) (restore func()) { origTimeNow := timeNow timeNow = f return func() { timeNow = origTimeNow } } + +func NewCmdShow(args ...string) *cmdShow { + cmdShow := &cmdShow{} + cmdShow.Positional.Repair = args + return cmdShow +} diff -Nru snapd-2.28.5/cmd/snap-repair/main.go snapd-2.29.3/cmd/snap-repair/main.go --- snapd-2.28.5/cmd/snap-repair/main.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/main.go 2017-10-23 06:17:27.000000000 +0000 @@ -27,6 +27,9 @@ // TODO: consider not using go-flags at all "github.com/jessevdk/go-flags" + "github.com/snapcore/snapd/cmd" + "github.com/snapcore/snapd/httputil" + "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/release" ) @@ -46,6 +49,13 @@ ` ) +func init() { + err := logger.SimpleSetup() + if err != nil { + fmt.Fprintf(Stderr, "WARNING: failed to activate logging: %v\n", err) + } +} + var errOnClassic = fmt.Errorf("cannot use snap-repair on a classic system") func main() { @@ -61,6 +71,7 @@ if release.OnClassic { return errOnClassic } + httputil.SetUserAgentFromVersion(cmd.Version, "snap-repair") if err := parseArgs(os.Args[1:]); err != nil { return err diff -Nru snapd-2.28.5/cmd/snap-repair/main_test.go snapd-2.29.3/cmd/snap-repair/main_test.go --- snapd-2.28.5/cmd/snap-repair/main_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/main_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -26,6 +26,7 @@ . "gopkg.in/check.v1" repair "github.com/snapcore/snapd/cmd/snap-repair" + "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/testutil" ) @@ -35,6 +36,9 @@ type repairSuite struct { testutil.BaseTest + baseRunnerSuite + + rootdir string stdout *bytes.Buffer stderr *bytes.Buffer @@ -42,6 +46,7 @@ func (r *repairSuite) SetUpTest(c *C) { r.BaseTest.SetUpTest(c) + r.baseRunnerSuite.SetUpTest(c) r.stdout = bytes.NewBuffer(nil) r.stderr = bytes.NewBuffer(nil) @@ -53,6 +58,10 @@ oldStderr := repair.Stderr r.AddCleanup(func() { repair.Stderr = oldStderr }) repair.Stderr = r.stderr + + r.rootdir = c.MkDir() + dirs.SetRootDir(r.rootdir) + r.AddCleanup(func() { dirs.SetRootDir("/") }) } func (r *repairSuite) Stdout() string { @@ -67,7 +76,7 @@ func (r *repairSuite) TestUnknownArg(c *C) { err := repair.ParseArgs([]string{}) - c.Check(err, ErrorMatches, "Please specify the run command") + c.Check(err, ErrorMatches, "Please specify one command of: list, run or show") } func (r *repairSuite) TestRunOnClassic(c *C) { diff -Nru snapd-2.28.5/cmd/snap-repair/runner.go snapd-2.29.3/cmd/snap-repair/runner.go --- snapd-2.28.5/cmd/snap-repair/runner.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/runner.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,6 +20,7 @@ package main import ( + "bufio" "bytes" "crypto/tls" "encoding/json" @@ -30,9 +31,11 @@ "net/http" "net/url" "os" + "os/exec" "path/filepath" "strconv" "strings" + "syscall" "time" "gopkg.in/retry.v1" @@ -41,6 +44,7 @@ "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/asserts/sysdb" "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/errtracker" "github.com/snapcore/snapd/httputil" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" @@ -48,6 +52,13 @@ "github.com/snapcore/snapd/strutil" ) +var ( + // TODO: move inside the repairs themselves? + defaultRepairTimeout = 30 * time.Minute +) + +var errtrackerReportRepair = errtracker.ReportRepair + // Repair is a runnable repair. type Repair struct { *asserts.Repair @@ -56,6 +67,14 @@ sequence int } +func (r *Repair) RunDir() string { + return filepath.Join(dirs.SnapRepairRunDir, r.BrandID(), strconv.Itoa(r.RepairID())) +} + +func (r *Repair) String() string { + return fmt.Sprintf("%s-%v", r.BrandID(), r.RepairID()) +} + // SetStatus sets the status of the repair in the state and saves the latter. func (r *Repair) SetStatus(status RepairStatus) { brandID := r.BrandID() @@ -65,26 +84,186 @@ r.run.SaveState() } +// makeRepairSymlink ensures $dir/repair exists and is a symlink to +// /usr/lib/snapd/snap-repair +func makeRepairSymlink(dir string) (err error) { + // make "repair" binary available to the repair scripts via symlink + // to the real snap-repair + if err = os.MkdirAll(dir, 0755); err != nil { + return err + } + + old := filepath.Join(dirs.CoreLibExecDir, "snap-repair") + new := filepath.Join(dir, "repair") + if err := os.Symlink(old, new); err != nil && !os.IsExist(err) { + return err + } + + return nil +} + // Run executes the repair script leaving execution trail files on disk. func (r *Repair) Run() error { - // XXX initial skeleton... // write the script to disk - rundir := filepath.Join(dirs.SnapRepairRunDir, r.BrandID(), r.RepairID()) + rundir := r.RunDir() err := os.MkdirAll(rundir, 0775) if err != nil { return err } - script := filepath.Join(rundir, fmt.Sprintf("script.r%d", r.Revision())) - err = osutil.AtomicWriteFile(script, r.Body(), 0600, 0) + + // ensure the script can use "repair done" + repairToolsDir := filepath.Join(dirs.SnapRunRepairDir, "tools") + if err := makeRepairSymlink(repairToolsDir); err != nil { + return err + } + + baseName := fmt.Sprintf("r%d", r.Revision()) + script := filepath.Join(rundir, baseName+".script") + err = osutil.AtomicWriteFile(script, r.Body(), 0700, 0) + if err != nil { + return err + } + + logPath := filepath.Join(rundir, baseName+".running") + logf, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return err + } + defer logf.Close() + + fmt.Fprintf(logf, "repair: %s\n", r) + fmt.Fprintf(logf, "revision: %d\n", r.Revision()) + fmt.Fprintf(logf, "summary: %s\n", r.Summary()) + fmt.Fprintf(logf, "output:\n") + + statusR, statusW, err := os.Pipe() if err != nil { return err } + defer statusR.Close() + defer statusW.Close() + + logger.Debugf("executing %s", script) + + // run the script + env := os.Environ() + // we need to hardcode FD=3 because this is the FD after + // exec.Command() forked. there is no way in go currently + // to run something right after fork() in the child to + // know the fd. However because go will close all fds + // except the ones in "cmd.ExtraFiles" we are safe to set "3" + env = append(env, "SNAP_REPAIR_STATUS_FD=3") + env = append(env, "SNAP_REPAIR_RUN_DIR="+rundir) + // inject repairToolDir into PATH so that the script can use + // `repair {done,skip,retry}` + var havePath bool + for i, envStr := range env { + if strings.HasPrefix(envStr, "PATH=") { + newEnv := fmt.Sprintf("%s:%s", strings.TrimSuffix(envStr, ":"), repairToolsDir) + env[i] = newEnv + havePath = true + } + } + if !havePath { + env = append(env, "PATH=/usr/sbin:/usr/bin:/sbin:/bin:"+repairToolsDir) + } + + workdir := filepath.Join(rundir, "work") + if err := os.MkdirAll(workdir, 0700); err != nil { + return err + } + + cmd := exec.Command(script) + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + cmd.Env = env + cmd.Dir = workdir + cmd.ExtraFiles = []*os.File{statusW} + cmd.Stdout = logf + cmd.Stderr = logf + if err = cmd.Start(); err != nil { + return err + } + statusW.Close() - // XXX actually run things and captures output etc + // wait for repair to finish or timeout + var scriptErr error + killTimerCh := time.After(defaultRepairTimeout) + doneCh := make(chan error) + go func() { + doneCh <- cmd.Wait() + close(doneCh) + }() + select { + case scriptErr = <-doneCh: + // done + case <-killTimerCh: + if err := osutil.KillProcessGroup(cmd); err != nil { + logger.Noticef("cannot kill timed out repair %s: %s", r, err) + } + scriptErr = fmt.Errorf("repair did not finish within %s", defaultRepairTimeout) + } + // read repair status pipe, use the last value + status := readStatus(statusR) + statusPath := filepath.Join(rundir, baseName+"."+status.String()) + + // if the script had an error exit status still honor what we + // read from the status-pipe, however report the error + if scriptErr != nil { + scriptErr = fmt.Errorf("repair %s revision %d failed: %s", r, r.Revision(), scriptErr) + if err := r.errtrackerReport(scriptErr, status, logPath); err != nil { + logger.Noticef("cannot report error to errtracker: %s", err) + } + // ensure the error is present in the output log + fmt.Fprintf(logf, "\n%s", scriptErr) + } + if err := os.Rename(logPath, statusPath); err != nil { + return err + } + r.SetStatus(status) return nil } +func readStatus(r io.Reader) RepairStatus { + var status RepairStatus + scanner := bufio.NewScanner(r) + for scanner.Scan() { + switch strings.TrimSpace(scanner.Text()) { + case "done": + status = DoneStatus + // TODO: support having a script skip over many and up to a given repair-id # + case "skip": + status = SkipStatus + } + } + if scanner.Err() != nil { + return RetryStatus + } + return status +} + +// errtrackerReport reports an repairErr with the given logPath to the +// snap error tracker. +func (r *Repair) errtrackerReport(repairErr error, status RepairStatus, logPath string) error { + errMsg := repairErr.Error() + + scriptOutput, err := ioutil.ReadFile(logPath) + if err != nil { + logger.Noticef("cannot read %s", logPath) + } + s := fmt.Sprintf("%s/%d", r.BrandID(), r.RepairID()) + + dupSig := fmt.Sprintf("%s\n%s\noutput:\n%s", s, errMsg, scriptOutput) + extra := map[string]string{ + "Revision": strconv.Itoa(r.Revision()), + "BrandID": r.BrandID(), + "RepairID": strconv.Itoa(r.RepairID()), + "Status": status.String(), + } + _, err = errtrackerReportRepair(s, errMsg, dupSig, extra) + return err +} + // Runner implements fetching, tracking and running repairs. type Runner struct { BaseURL *url.URL @@ -141,8 +320,8 @@ // auxiliary assertions. If revision>=0 the request will include an // If-None-Match header with an ETag for the revision, and // ErrRepairNotModified is returned if the revision is still current. -func (run *Runner) Fetch(brandID, repairID string, revision int) (repair *asserts.Repair, aux []asserts.Assertion, err error) { - u, err := run.BaseURL.Parse(fmt.Sprintf("repairs/%s/%s", brandID, repairID)) +func (run *Runner) Fetch(brandID string, repairID int, revision int) (*asserts.Repair, []asserts.Assertion, error) { + u, err := run.BaseURL.Parse(fmt.Sprintf("repairs/%s/%d", brandID, repairID)) if err != nil { return nil, nil, err } @@ -160,6 +339,7 @@ return run.cli.Do(req) }, func(resp *http.Response) error { if resp.StatusCode == 200 { + logger.Debugf("fetching repair %s-%d", brandID, repairID) // decode assertions dec := asserts.NewDecoderWithTypeMaxBodySize(resp.Body, map[*asserts.AssertionType]int{ asserts.RepairType: maxRepairScriptSize, @@ -206,7 +386,7 @@ return nil, nil, fmt.Errorf("cannot fetch repair, unexpected status %d", resp.StatusCode) } - repair, aux, err = checkStream(brandID, repairID, r) + repair, aux, err := checkStream(brandID, repairID, r) if err != nil { return nil, nil, fmt.Errorf("cannot fetch repair, %v", err) } @@ -218,10 +398,10 @@ return nil, nil, ErrRepairNotModified } - return + return repair, aux, err } -func checkStream(brandID, repairID string, r []asserts.Assertion) (repair *asserts.Repair, aux []asserts.Assertion, err error) { +func checkStream(brandID string, repairID int, r []asserts.Assertion) (repair *asserts.Repair, aux []asserts.Assertion, err error) { if len(r) == 0 { return nil, nil, fmt.Errorf("empty repair assertions stream") } @@ -232,7 +412,7 @@ } if repair.BrandID() != brandID || repair.RepairID() != repairID { - return nil, nil, fmt.Errorf("repair id mismatch %s/%s != %s/%s", repair.BrandID(), repair.RepairID(), brandID, repairID) + return nil, nil, fmt.Errorf("repair id mismatch %s/%d != %s/%d", repair.BrandID(), repair.RepairID(), brandID, repairID) } return repair, r[1:], nil @@ -243,8 +423,8 @@ } // Peek retrieves the headers for the repair with the given ids. -func (run *Runner) Peek(brandID, repairID string) (headers map[string]interface{}, err error) { - u, err := run.BaseURL.Parse(fmt.Sprintf("repairs/%s/%s", brandID, repairID)) +func (run *Runner) Peek(brandID string, repairID int) (headers map[string]interface{}, err error) { + u, err := run.BaseURL.Parse(fmt.Sprintf("repairs/%s/%d", brandID, repairID)) if err != nil { return nil, err } @@ -290,8 +470,8 @@ } headers = rsp.Headers - if headers["brand-id"] != brandID || headers["repair-id"] != repairID { - return nil, fmt.Errorf("cannot peek repair headers, repair id mismatch %s/%s != %s/%s", headers["brand-id"], headers["repair-id"], brandID, repairID) + if headers["brand-id"] != brandID || headers["repair-id"] != strconv.Itoa(repairID) { + return nil, fmt.Errorf("cannot peek repair headers, repair id mismatch %s/%s != %s/%d", headers["brand-id"], headers["repair-id"], brandID, repairID) } return headers, nil @@ -312,6 +492,19 @@ DoneStatus ) +func (rs RepairStatus) String() string { + switch rs { + case RetryStatus: + return "retry" + case SkipStatus: + return "skip" + case DoneStatus: + return "done" + default: + return "unknown" + } +} + // RepairState holds the current revision and status of a repair in a sequence of repairs. type RepairState struct { Sequence int `json:"sequence"` @@ -405,10 +598,10 @@ // a trusted authority acctID := a.AuthorityID() _, err := trusted.Get(asserts.AccountType, []string{acctID}, asserts.AccountType.MaxSupportedFormat()) - if err != nil && err != asserts.ErrNotFound { + if err != nil && !asserts.IsNotFound(err) { return err } - if err == asserts.ErrNotFound { + if asserts.IsNotFound(err) { return fmt.Errorf("%v not signed by trusted authority: %s", a.Ref(), acctID) } return nil @@ -430,17 +623,17 @@ seen[u] = true signKey := []string{a.SignKeyID()} key, err := trusted.Get(asserts.AccountKeyType, signKey, acctKeyMaxSuppFormat) - if err != nil && err != asserts.ErrNotFound { + if err != nil && !asserts.IsNotFound(err) { return err } if err == nil { bottom = true } else { key, err = workBS.Get(asserts.AccountKeyType, signKey, acctKeyMaxSuppFormat) - if err != nil && err != asserts.ErrNotFound { + if err != nil && !asserts.IsNotFound(err) { return err } - if err == asserts.ErrNotFound { + if asserts.IsNotFound(err) { return fmt.Errorf("cannot find public key %q", signKey[0]) } if err := checkAuthorityID(key, trusted); err != nil { @@ -567,6 +760,9 @@ // Applicable returns whether a repair with the given headers is applicable to the device. func (run *Runner) Applicable(headers map[string]interface{}) bool { + if headers["disabled"] == "true" { + return false + } series, err := stringList(headers, "series") if err != nil { return false @@ -606,8 +802,7 @@ var errSkip = errors.New("repair unnecessary on this system") -func (run *Runner) fetch(brandID string, seq int) (repair *asserts.Repair, aux []asserts.Assertion, err error) { - repairID := strconv.Itoa(seq) +func (run *Runner) fetch(brandID string, repairID int) (repair *asserts.Repair, aux []asserts.Assertion, err error) { headers, err := run.Peek(brandID, repairID) if err != nil { return nil, nil, err @@ -618,14 +813,12 @@ return run.Fetch(brandID, repairID, -1) } -func (run *Runner) refetch(brandID string, seq, revision int) (repair *asserts.Repair, aux []asserts.Assertion, err error) { - repairID := strconv.Itoa(seq) +func (run *Runner) refetch(brandID string, repairID, revision int) (repair *asserts.Repair, aux []asserts.Assertion, err error) { return run.Fetch(brandID, repairID, revision) } -func (run *Runner) saveStream(brandID string, seq int, repair *asserts.Repair, aux []asserts.Assertion) error { - repairID := strconv.Itoa(seq) - d := filepath.Join(dirs.SnapRepairAssertsDir, brandID, repairID) +func (run *Runner) saveStream(brandID string, repairID int, repair *asserts.Repair, aux []asserts.Assertion) error { + d := filepath.Join(dirs.SnapRepairAssertsDir, brandID, strconv.Itoa(repairID)) err := os.MkdirAll(d, 0775) if err != nil { return err @@ -635,17 +828,16 @@ r := append([]asserts.Assertion{repair}, aux...) for _, a := range r { if err := enc.Encode(a); err != nil { - return fmt.Errorf("cannot encode repair assertions %s-%s for saving: %v", brandID, repairID, err) + return fmt.Errorf("cannot encode repair assertions %s-%d for saving: %v", brandID, repairID, err) } } - p := filepath.Join(d, fmt.Sprintf("repair.r%d", r[0].Revision())) + p := filepath.Join(d, fmt.Sprintf("r%d.repair", r[0].Revision())) return osutil.AtomicWriteFile(p, buf.Bytes(), 0600, 0) } -func (run *Runner) readSavedStream(brandID string, seq, revision int) (repair *asserts.Repair, aux []asserts.Assertion, err error) { - repairID := strconv.Itoa(seq) - d := filepath.Join(dirs.SnapRepairAssertsDir, brandID, repairID) - p := filepath.Join(d, fmt.Sprintf("repair.r%d", revision)) +func (run *Runner) readSavedStream(brandID string, repairID, revision int) (repair *asserts.Repair, aux []asserts.Assertion, err error) { + d := filepath.Join(dirs.SnapRepairAssertsDir, brandID, strconv.Itoa(repairID)) + p := filepath.Join(d, fmt.Sprintf("r%d.repair", revision)) f, err := os.Open(p) if err != nil { return nil, nil, err @@ -660,7 +852,7 @@ break } if err != nil { - return nil, nil, fmt.Errorf("cannot decode repair assertions %s-%s from disk: %v", brandID, repairID, err) + return nil, nil, fmt.Errorf("cannot decode repair assertions %s-%d from disk: %v", brandID, repairID, err) } r = append(r, a) } diff -Nru snapd-2.28.5/cmd/snap-repair/runner_test.go snapd-2.29.3/cmd/snap-repair/runner_test.go --- snapd-2.28.5/cmd/snap-repair/runner_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/runner_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -43,10 +43,11 @@ "github.com/snapcore/snapd/asserts/sysdb" repair "github.com/snapcore/snapd/cmd/snap-repair" "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" ) -type runnerSuite struct { +type baseRunnerSuite struct { tmpdir string seedTime time.Time @@ -66,11 +67,11 @@ repairsAcctKey *asserts.AccountKey repairsSigning *assertstest.SigningDB -} -var _ = Suite(&runnerSuite{}) + restoreLogger func() +} -func (s *runnerSuite) SetUpSuite(c *C) { +func (s *baseRunnerSuite) SetUpSuite(c *C) { s.storeSigning = assertstest.NewStoreStack("canonical", nil) brandPrivKey, _ := assertstest.GenerateKey(752) @@ -106,7 +107,9 @@ s.repairsSigning = assertstest.NewSigningDB("canonical", repairsKey) } -func (s *runnerSuite) SetUpTest(c *C) { +func (s *baseRunnerSuite) SetUpTest(c *C) { + _, s.restoreLogger = logger.MockLogger() + s.tmpdir = c.MkDir() dirs.SetRootDir(s.tmpdir) @@ -127,10 +130,42 @@ s.t0 = time.Now().UTC().Truncate(time.Minute) } -func (s *runnerSuite) TearDownTest(c *C) { +func (s *baseRunnerSuite) TearDownTest(c *C) { dirs.SetRootDir("/") + s.restoreLogger() +} + +func (s *baseRunnerSuite) signSeqRepairs(c *C, repairs []string) []string { + var seq []string + for _, rpr := range repairs { + decoded, err := asserts.Decode([]byte(rpr)) + c.Assert(err, IsNil) + signed, err := s.repairsSigning.Sign(asserts.RepairType, decoded.Headers(), decoded.Body(), "") + c.Assert(err, IsNil) + buf := &bytes.Buffer{} + enc := asserts.NewEncoder(buf) + enc.Encode(signed) + enc.Encode(s.repairsAcctKey) + seq = append(seq, buf.String()) + } + return seq } +const freshStateJSON = `{"device":{"brand":"my-brand","model":"my-model"},"time-lower-bound":"2017-08-11T15:49:49Z"}` + +func (s *baseRunnerSuite) freshState(c *C) { + err := os.MkdirAll(dirs.SnapRepairDir, 0775) + c.Assert(err, IsNil) + err = ioutil.WriteFile(dirs.SnapRepairStateFile, []byte(freshStateJSON), 0600) + c.Assert(err, IsNil) +} + +type runnerSuite struct { + baseRunnerSuite +} + +var _ = Suite(&runnerSuite{}) + var ( testKey = `type: account-key authority-id: canonical @@ -151,6 +186,7 @@ authority-id: canonical brand-id: canonical repair-id: 2 +summary: repair two architectures: - amd64 - arm64 @@ -208,12 +244,12 @@ r := s.mockBrokenTimeNowSetToEpoch(c, runner) defer r() - repair, aux, err := runner.Fetch("canonical", "2", -1) + repair, aux, err := runner.Fetch("canonical", 2, -1) c.Assert(err, IsNil) c.Check(repair, NotNil) c.Check(aux, HasLen, 0) c.Check(repair.BrandID(), Equals, "canonical") - c.Check(repair.RepairID(), Equals, "2") + c.Check(repair.RepairID(), Equals, 2) c.Check(repair.Body(), DeepEquals, []byte("script\n")) s.checkBrokenTimeNowMitigated(c, runner) @@ -237,7 +273,7 @@ runner := repair.NewRunner() runner.BaseURL = mustParseURL(mockServer.URL) - _, _, err := runner.Fetch("canonical", "2", -1) + _, _, err := runner.Fetch("canonical", 2, -1) c.Assert(err, ErrorMatches, `assertion body length 7 exceeds maximum body size 4 for "repair".*`) c.Assert(n, Equals, 1) } @@ -267,7 +303,7 @@ runner := repair.NewRunner() runner.BaseURL = mustParseURL(mockServer.URL) - _, _, err := runner.Fetch("canonical", "2", -1) + _, _, err := runner.Fetch("canonical", 2, -1) c.Assert(err, ErrorMatches, "cannot fetch repair, unexpected status 500") c.Assert(n, Equals, 5) } @@ -288,7 +324,7 @@ runner := repair.NewRunner() runner.BaseURL = mustParseURL(mockServer.URL) - _, _, err := runner.Fetch("canonical", "2", -1) + _, _, err := runner.Fetch("canonical", 2, -1) c.Assert(err, Equals, io.ErrUnexpectedEOF) c.Assert(n, Equals, 5) } @@ -310,7 +346,7 @@ runner := repair.NewRunner() runner.BaseURL = mustParseURL(mockServer.URL) - _, _, err := runner.Fetch("canonical", "2", -1) + _, _, err := runner.Fetch("canonical", 2, -1) c.Assert(err, Equals, io.ErrUnexpectedEOF) c.Assert(n, Equals, 5) } @@ -334,7 +370,7 @@ r := s.mockBrokenTimeNowSetToEpoch(c, runner) defer r() - _, _, err := runner.Fetch("canonical", "2", -1) + _, _, err := runner.Fetch("canonical", 2, -1) c.Assert(err, Equals, repair.ErrRepairNotFound) c.Assert(n, Equals, 1) @@ -361,7 +397,7 @@ r := s.mockBrokenTimeNowSetToEpoch(c, runner) defer r() - _, _, err := runner.Fetch("canonical", "2", 0) + _, _, err := runner.Fetch("canonical", 2, 0) c.Assert(err, Equals, repair.ErrRepairNotModified) c.Assert(n, Equals, 1) @@ -379,7 +415,7 @@ runner := repair.NewRunner() runner.BaseURL = mustParseURL(mockServer.URL) - _, _, err := runner.Fetch("canonical", "2", 2) + _, _, err := runner.Fetch("canonical", 2, 2) c.Assert(err, Equals, repair.ErrRepairNotModified) } @@ -395,7 +431,7 @@ runner := repair.NewRunner() runner.BaseURL = mustParseURL(mockServer.URL) - _, _, err := runner.Fetch("canonical", "4", -1) + _, _, err := runner.Fetch("canonical", 4, -1) c.Assert(err, ErrorMatches, `cannot fetch repair, repair id mismatch canonical/2 != canonical/4`) } @@ -412,7 +448,7 @@ runner := repair.NewRunner() runner.BaseURL = mustParseURL(mockServer.URL) - _, _, err := runner.Fetch("canonical", "2", -1) + _, _, err := runner.Fetch("canonical", 2, -1) c.Assert(err, ErrorMatches, `cannot fetch repair, unexpected first assertion "account-key"`) } @@ -431,7 +467,7 @@ runner := repair.NewRunner() runner.BaseURL = mustParseURL(mockServer.URL) - repair, aux, err := runner.Fetch("canonical", "2", -1) + repair, aux, err := runner.Fetch("canonical", 2, -1) c.Assert(err, IsNil) c.Check(repair, NotNil) c.Check(aux, HasLen, 1) @@ -455,7 +491,7 @@ r := s.mockBrokenTimeNowSetToEpoch(c, runner) defer r() - h, err := runner.Peek("canonical", "2") + h, err := runner.Peek("canonical", 2) c.Assert(err, IsNil) c.Check(h["series"], DeepEquals, []interface{}{"16"}) c.Check(h["architectures"], DeepEquals, []interface{}{"amd64", "arm64"}) @@ -480,7 +516,7 @@ runner := repair.NewRunner() runner.BaseURL = mustParseURL(mockServer.URL) - _, err := runner.Peek("canonical", "2") + _, err := runner.Peek("canonical", 2) c.Assert(err, ErrorMatches, "cannot peek repair headers, unexpected status 500") c.Assert(n, Equals, 5) } @@ -502,7 +538,7 @@ runner := repair.NewRunner() runner.BaseURL = mustParseURL(mockServer.URL) - _, err := runner.Peek("canonical", "2") + _, err := runner.Peek("canonical", 2) c.Assert(err, Equals, io.ErrUnexpectedEOF) c.Assert(n, Equals, 5) } @@ -523,7 +559,7 @@ r := s.mockBrokenTimeNowSetToEpoch(c, runner) defer r() - _, err := runner.Peek("canonical", "2") + _, err := runner.Peek("canonical", 2) c.Assert(err, Equals, repair.ErrRepairNotFound) c.Assert(n, Equals, 1) @@ -542,19 +578,10 @@ runner := repair.NewRunner() runner.BaseURL = mustParseURL(mockServer.URL) - _, err := runner.Peek("canonical", "4") + _, err := runner.Peek("canonical", 4) c.Assert(err, ErrorMatches, `cannot peek repair headers, repair id mismatch canonical/2 != canonical/4`) } -const freshStateJSON = `{"device":{"brand":"my-brand","model":"my-model"},"time-lower-bound":"2017-08-11T15:49:49Z"}` - -func (s *runnerSuite) freshState(c *C) { - err := os.MkdirAll(dirs.SnapRepairDir, 0775) - c.Assert(err, IsNil) - err = ioutil.WriteFile(dirs.SnapRepairStateFile, []byte(freshStateJSON), 0600) - c.Assert(err, IsNil) -} - func (s *runnerSuite) TestLoadState(c *C) { s.freshState(c) @@ -659,14 +686,27 @@ c.Check(runner.TLSTime().Equal(s.seedTime), Equals, true) } -func (s *runnerSuite) TestLoadStateInitStateFail(c *C) { - par := filepath.Dir(dirs.SnapSeedDir) - err := os.Chmod(par, 0555) +func makeReadOnly(c *C, dir string) (restore func()) { + // skip tests that need this because uid==0 does not honor + // write permissions in directories (yay, unix) + if os.Getuid() == 0 { + // FIXME: we could use osutil.Chattr() here + c.Skip("too lazy to make path readonly as root") + } + err := os.Chmod(dir, 0555) c.Assert(err, IsNil) - defer os.Chmod(par, 0775) + return func() { + err := os.Chmod(dir, 0755) + c.Assert(err, IsNil) + } +} + +func (s *runnerSuite) TestLoadStateInitStateFail(c *C) { + restore := makeReadOnly(c, filepath.Dir(dirs.SnapSeedDir)) + defer restore() runner := repair.NewRunner() - err = runner.LoadState() + err := runner.LoadState() c.Check(err, ErrorMatches, `cannot create repair state directory:.*`) } @@ -677,9 +717,8 @@ err := runner.LoadState() c.Assert(err, IsNil) - err = os.Chmod(dirs.SnapRepairDir, 0555) - c.Assert(err, IsNil) - defer os.Chmod(dirs.SnapRepairDir, 0775) + restore := makeReadOnly(c, dirs.SnapRepairDir) + defer restore() // no error because this is a no-op err = runner.SaveState() @@ -742,6 +781,8 @@ {map[string]interface{}{"models": []interface{}{"my-brand/xxx*"}}, false}, {map[string]interface{}{"models": []interface{}{"my-brand/my-mod*", "my-brand/xxx*"}}, true}, {map[string]interface{}{"models": []interface{}{"my*"}}, false}, + {map[string]interface{}{"disabled": "true"}, false}, + {map[string]interface{}{"disabled": "false"}, true}, } for _, scen := range scenarios { @@ -755,6 +796,7 @@ authority-id: canonical brand-id: canonical repair-id: 1 +summary: repair one timestamp: 2017-07-01T12:00:00Z body-length: 8 sign-key-sha3-384: KPIl7M4vQ9d4AUjkoU41TGAwtOMLc_bWUCeW8AvdRWD4_xcP60Oo4ABsFNo6BtXj @@ -767,6 +809,7 @@ authority-id: canonical brand-id: canonical repair-id: 2 +summary: repair two series: - 33 timestamp: 2017-07-02T12:00:00Z @@ -782,6 +825,7 @@ authority-id: canonical brand-id: canonical repair-id: 3 +summary: repair three rev2 series: - 16 timestamp: 2017-07-03T12:00:00Z @@ -799,6 +843,7 @@ authority-id: canonical brand-id: canonical repair-id: 3 +summary: repair three rev4 series: - 16 timestamp: 2017-07-03T12:00:00Z @@ -815,6 +860,7 @@ authority-id: canonical brand-id: canonical repair-id: 4 +summary: repair four timestamp: 2017-07-03T12:00:00Z body-length: 8 sign-key-sha3-384: KPIl7M4vQ9d4AUjkoU41TGAwtOMLc_bWUCeW8AvdRWD4_xcP60Oo4ABsFNo6BtXj @@ -878,6 +924,13 @@ return mockServer } +func (s *runnerSuite) TestTrustedRepairRootKeys(c *C) { + acctKeys := repair.TrustedRepairRootKeys() + c.Check(acctKeys, HasLen, 1) + c.Check(acctKeys[0].AccountID(), Equals, "canonical") + c.Check(acctKeys[0].PublicKeyID(), Equals, "nttW6NfBXI_E-00u38W-KH6eiksfQNXuI7IiumoV49_zkbhM0sYTzSnFlwZC-W4t") +} + func (s *runnerSuite) TestVerify(c *C) { r1 := sysdb.InjectTrusted(s.storeSigning.Trusted) defer r1() @@ -889,6 +942,7 @@ a, err := s.repairsSigning.Sign(asserts.RepairType, map[string]interface{}{ "brand-id": "canonical", "repair-id": "2", + "summary": "repair two", "timestamp": time.Now().UTC().Format(time.RFC3339), }, []byte("#script"), "") c.Assert(err, IsNil) @@ -942,13 +996,13 @@ rpr, err := runner.Next("canonical") c.Assert(err, IsNil) - c.Check(rpr.RepairID(), Equals, "1") - c.Check(osutil.FileExists(filepath.Join(dirs.SnapRepairAssertsDir, "canonical", "1", "repair.r0")), Equals, true) + c.Check(rpr.RepairID(), Equals, 1) + c.Check(osutil.FileExists(filepath.Join(dirs.SnapRepairAssertsDir, "canonical", "1", "r0.repair")), Equals, true) rpr, err = runner.Next("canonical") c.Assert(err, IsNil) - c.Check(rpr.RepairID(), Equals, "3") - strm, err := ioutil.ReadFile(filepath.Join(dirs.SnapRepairAssertsDir, "canonical", "3", "repair.r2")) + c.Check(rpr.RepairID(), Equals, 3) + strm, err := ioutil.ReadFile(filepath.Join(dirs.SnapRepairAssertsDir, "canonical", "3", "r2.repair")) c.Assert(err, IsNil) c.Check(string(strm), Equals, seqRepairs[2]) @@ -978,11 +1032,11 @@ rpr, err = runner.Next("canonical") c.Assert(err, IsNil) - c.Check(rpr.RepairID(), Equals, "1") + c.Check(rpr.RepairID(), Equals, 1) rpr, err = runner.Next("canonical") c.Assert(err, IsNil) - c.Check(rpr.RepairID(), Equals, "3") + c.Check(rpr.RepairID(), Equals, 3) // refetched new revision! c.Check(rpr.Revision(), Equals, 4) c.Check(rpr.Body(), DeepEquals, []byte("scriptC2\n")) @@ -990,7 +1044,7 @@ // new repair rpr, err = runner.Next("canonical") c.Assert(err, IsNil) - c.Check(rpr.RepairID(), Equals, "4") + c.Check(rpr.RepairID(), Equals, 4) c.Check(rpr.Body(), DeepEquals, []byte("scriptD\n")) // no more @@ -1020,6 +1074,7 @@ authority-id: canonical brand-id: canonical repair-id: 1 +summary: repair one series: - 33 timestamp: 2017-07-02T12:00:00Z @@ -1056,6 +1111,7 @@ authority-id: canonical brand-id: canonical repair-id: 1 +summary: repair one series: - 16 timestamp: 2017-07-02T12:00:00Z @@ -1098,8 +1154,10 @@ revision: 1 brand-id: canonical repair-id: 1 +summary: repair one rev1 series: - - 33 + - 16 +disabled: true timestamp: 2017-07-02T12:00:00Z body-length: 7 sign-key-sha3-384: KPIl7M4vQ9d4AUjkoU41TGAwtOMLc_bWUCeW8AvdRWD4_xcP60Oo4ABsFNo6BtXj @@ -1184,6 +1242,7 @@ authority-id: canonical brand-id: canonical repair-id: 1 +summary: repair one series: - 33 timestamp: 2017-07-02T12:00:00Z @@ -1203,11 +1262,10 @@ runner.LoadState() // break SaveState - err := os.Chmod(dirs.SnapRepairDir, 0555) - c.Assert(err, IsNil) - defer os.Chmod(dirs.SnapRepairDir, 0775) + restore := makeReadOnly(c, dirs.SnapRepairDir) + defer restore() - _, err = runner.Next("canonical") + _, err := runner.Next("canonical") c.Check(err, ErrorMatches, `cannot save repair state:.*`) } @@ -1216,6 +1274,7 @@ authority-id: canonical brand-id: canonical repair-id: 1 +summary: repair one timestamp: 2017-07-02T12:00:00Z body-length: 8 sign-key-sha3-384: KPIl7M4vQ9d4AUjkoU41TGAwtOMLc_bWUCeW8AvdRWD4_xcP60Oo4ABsFNo6BtXj @@ -1256,6 +1315,7 @@ rpr, err := randomSigning.Sign(asserts.RepairType, map[string]interface{}{ "brand-id": "canonical", "repair-id": "1", + "summary": "repair one", "timestamp": time.Now().UTC().Format(time.RFC3339), }, []byte("scriptB\n"), "") c.Assert(err, IsNil) @@ -1308,7 +1368,7 @@ rpr, err := runner.Next("canonical") c.Assert(err, IsNil) - c.Check(rpr.RepairID(), Equals, "1") + c.Check(rpr.RepairID(), Equals, 1) } func (s *runnerSuite) TestRepairSetStatus(c *C) { @@ -1316,6 +1376,7 @@ authority-id: canonical brand-id: canonical repair-id: 1 +summary: repair one timestamp: 2017-07-02T12:00:00Z body-length: 8 sign-key-sha3-384: KPIl7M4vQ9d4AUjkoU41TGAwtOMLc_bWUCeW8AvdRWD4_xcP60Oo4ABsFNo6BtXj @@ -1358,6 +1419,7 @@ authority-id: canonical brand-id: canonical repair-id: 1 +summary: repair one series: - 16 timestamp: 2017-07-02T12:00:00Z @@ -1387,7 +1449,328 @@ c.Assert(err, IsNil) rpr.Run() - scrpt, err := ioutil.ReadFile(filepath.Join(dirs.SnapRepairRunDir, "canonical", "1", "script.r0")) + scrpt, err := ioutil.ReadFile(filepath.Join(dirs.SnapRepairRunDir, "canonical", "1", "r0.script")) c.Assert(err, IsNil) c.Check(string(scrpt), Equals, "exit 0\n") } + +func makeMockRepair(script string) string { + return fmt.Sprintf(`type: repair +authority-id: canonical +brand-id: canonical +repair-id: 1 +summary: repair one +series: + - 16 +timestamp: 2017-07-02T12:00:00Z +body-length: %d +sign-key-sha3-384: KPIl7M4vQ9d4AUjkoU41TGAwtOMLc_bWUCeW8AvdRWD4_xcP60Oo4ABsFNo6BtXj + +%s + +AXNpZw==`, len(script), script) +} + +func verifyRepairStatus(c *C, status repair.RepairStatus) { + data, err := ioutil.ReadFile(dirs.SnapRepairStateFile) + c.Assert(err, IsNil) + c.Check(string(data), Matches, fmt.Sprintf(`{"device":{"brand":"","model":""},"sequences":{"canonical":\[{"sequence":1,"revision":0,"status":%d}.*`, status)) +} + +// tests related to correct execution of script +type runScriptSuite struct { + baseRunnerSuite + + seqRepairs []string + + mockServer *httptest.Server + runner *repair.Runner + + runDir string + + restoreErrTrackerReportRepair func() + errReport struct { + repair string + errMsg string + dupSig string + extra map[string]string + } +} + +var _ = Suite(&runScriptSuite{}) + +func (s *runScriptSuite) SetUpTest(c *C) { + s.baseRunnerSuite.SetUpTest(c) + + s.mockServer = makeMockServer(c, &s.seqRepairs, false) + + s.runner = repair.NewRunner() + s.runner.BaseURL = mustParseURL(s.mockServer.URL) + s.runner.LoadState() + + s.runDir = filepath.Join(dirs.SnapRepairRunDir, "canonical", "1") + + s.restoreErrTrackerReportRepair = repair.MockErrtrackerReportRepair(s.errtrackerReportRepair) +} + +func (s *runScriptSuite) TearDownTest(c *C) { + s.baseRunnerSuite.TearDownTest(c) + + s.restoreErrTrackerReportRepair() + s.mockServer.Close() +} + +func (s *runScriptSuite) errtrackerReportRepair(repair, errMsg, dupSig string, extra map[string]string) (string, error) { + s.errReport.repair = repair + s.errReport.errMsg = errMsg + s.errReport.dupSig = dupSig + s.errReport.extra = extra + + return "some-oops-id", nil +} + +func (s *runScriptSuite) testScriptRun(c *C, mockScript string) *repair.Repair { + r1 := sysdb.InjectTrusted(s.storeSigning.Trusted) + defer r1() + r2 := repair.MockTrustedRepairRootKeys([]*asserts.AccountKey{s.repairRootAcctKey}) + defer r2() + + s.seqRepairs = s.signSeqRepairs(c, s.seqRepairs) + + rpr, err := s.runner.Next("canonical") + c.Assert(err, IsNil) + + err = rpr.Run() + c.Assert(err, IsNil) + + scrpt, err := ioutil.ReadFile(filepath.Join(s.runDir, "r0.script")) + c.Assert(err, IsNil) + c.Check(string(scrpt), Equals, mockScript) + + return rpr +} + +func (s *runScriptSuite) verifyRundir(c *C, names []string) { + dirents, err := ioutil.ReadDir(s.runDir) + c.Assert(err, IsNil) + c.Assert(dirents, HasLen, len(names)) + for i := range dirents { + c.Check(dirents[i].Name(), Matches, names[i]) + } +} + +type byMtime []os.FileInfo + +func (m byMtime) Len() int { return len(m) } +func (m byMtime) Less(i, j int) bool { return m[i].ModTime().Before(m[j].ModTime()) } +func (m byMtime) Swap(i, j int) { m[i], m[j] = m[j], m[i] } + +func (s *runScriptSuite) verifyOutput(c *C, name, expectedOutput string) { + output, err := ioutil.ReadFile(filepath.Join(s.runDir, name)) + c.Assert(err, IsNil) + c.Check(string(output), Equals, expectedOutput) + // ensure correct permissions + fi, err := os.Stat(filepath.Join(s.runDir, name)) + c.Assert(err, IsNil) + c.Check(fi.Mode(), Equals, os.FileMode(0600)) +} + +func (s *runScriptSuite) TestRepairBasicRunHappy(c *C) { + script := `#!/bin/sh +echo "happy output" +echo "done" >&$SNAP_REPAIR_STATUS_FD +exit 0 +` + s.seqRepairs = []string{makeMockRepair(script)} + s.testScriptRun(c, script) + // verify + s.verifyRundir(c, []string{ + `^r0.done$`, + `^r0.script$`, + `^work$`, + }) + s.verifyOutput(c, "r0.done", `repair: canonical-1 +revision: 0 +summary: repair one +output: +happy output +`) + verifyRepairStatus(c, repair.DoneStatus) +} + +func (s *runScriptSuite) TestRepairBasicRunUnhappy(c *C) { + script := `#!/bin/sh +echo "unhappy output" +exit 1 +` + s.seqRepairs = []string{makeMockRepair(script)} + s.testScriptRun(c, script) + // verify + s.verifyRundir(c, []string{ + `^r0.retry$`, + `^r0.script$`, + `^work$`, + }) + s.verifyOutput(c, "r0.retry", `repair: canonical-1 +revision: 0 +summary: repair one +output: +unhappy output + +repair canonical-1 revision 0 failed: exit status 1`) + verifyRepairStatus(c, repair.RetryStatus) + + c.Check(s.errReport.repair, Equals, "canonical/1") + c.Check(s.errReport.errMsg, Equals, `repair canonical-1 revision 0 failed: exit status 1`) + c.Check(s.errReport.dupSig, Equals, `canonical/1 +repair canonical-1 revision 0 failed: exit status 1 +output: +repair: canonical-1 +revision: 0 +summary: repair one +output: +unhappy output +`) + c.Check(s.errReport.extra, DeepEquals, map[string]string{ + "Revision": "0", + "RepairID": "1", + "BrandID": "canonical", + "Status": "retry", + }) +} + +func (s *runScriptSuite) TestRepairBasicSkip(c *C) { + script := `#!/bin/sh +echo "other output" +echo "skip" >&$SNAP_REPAIR_STATUS_FD +exit 0 +` + s.seqRepairs = []string{makeMockRepair(script)} + s.testScriptRun(c, script) + // verify + s.verifyRundir(c, []string{ + `^r0.script$`, + `^r0.skip$`, + `^work$`, + }) + s.verifyOutput(c, "r0.skip", `repair: canonical-1 +revision: 0 +summary: repair one +output: +other output +`) + verifyRepairStatus(c, repair.SkipStatus) +} + +func (s *runScriptSuite) TestRepairBasicRunUnhappyThenHappy(c *C) { + script := `#!/bin/sh +if [ -f zzz-ran-once ]; then + echo "happy now" + echo "done" >&$SNAP_REPAIR_STATUS_FD + exit 0 +fi +echo "unhappy output" +touch zzz-ran-once +exit 1 +` + s.seqRepairs = []string{makeMockRepair(script)} + rpr := s.testScriptRun(c, script) + s.verifyRundir(c, []string{ + `^r0.retry$`, + `^r0.script$`, + `^work$`, + }) + s.verifyOutput(c, "r0.retry", `repair: canonical-1 +revision: 0 +summary: repair one +output: +unhappy output + +repair canonical-1 revision 0 failed: exit status 1`) + verifyRepairStatus(c, repair.RetryStatus) + + // run again, it will be happy this time + err := rpr.Run() + c.Assert(err, IsNil) + + s.verifyRundir(c, []string{ + `^r0.done$`, + `^r0.retry$`, + `^r0.script$`, + `^work$`, + }) + s.verifyOutput(c, "r0.done", `repair: canonical-1 +revision: 0 +summary: repair one +output: +happy now +`) + verifyRepairStatus(c, repair.DoneStatus) +} + +func (s *runScriptSuite) TestRepairHitsTimeout(c *C) { + r1 := sysdb.InjectTrusted(s.storeSigning.Trusted) + defer r1() + r2 := repair.MockTrustedRepairRootKeys([]*asserts.AccountKey{s.repairRootAcctKey}) + defer r2() + + restore := repair.MockDefaultRepairTimeout(100 * time.Millisecond) + defer restore() + + script := `#!/bin/sh +echo "output before timeout" +sleep 100 +` + s.seqRepairs = []string{makeMockRepair(script)} + s.seqRepairs = s.signSeqRepairs(c, s.seqRepairs) + + rpr, err := s.runner.Next("canonical") + c.Assert(err, IsNil) + + err = rpr.Run() + c.Assert(err, IsNil) + + s.verifyRundir(c, []string{ + `^r0.retry$`, + `^r0.script$`, + `^work$`, + }) + s.verifyOutput(c, "r0.retry", `repair: canonical-1 +revision: 0 +summary: repair one +output: +output before timeout + +repair canonical-1 revision 0 failed: repair did not finish within 100ms`) + verifyRepairStatus(c, repair.RetryStatus) +} + +func (s *runScriptSuite) TestRepairHasCorrectPath(c *C) { + r1 := sysdb.InjectTrusted(s.storeSigning.Trusted) + defer r1() + r2 := repair.MockTrustedRepairRootKeys([]*asserts.AccountKey{s.repairRootAcctKey}) + defer r2() + + script := `#!/bin/sh +echo PATH=$PATH +ls -l ${PATH##*:}/repair +` + s.seqRepairs = []string{makeMockRepair(script)} + s.seqRepairs = s.signSeqRepairs(c, s.seqRepairs) + + rpr, err := s.runner.Next("canonical") + c.Assert(err, IsNil) + + err = rpr.Run() + c.Assert(err, IsNil) + + output, err := ioutil.ReadFile(filepath.Join(s.runDir, "r0.retry")) + c.Assert(err, IsNil) + c.Check(string(output), Matches, fmt.Sprintf(`(?ms).*^PATH=.*:.*/run/snapd/repair/tools.*`)) + c.Check(string(output), Matches, `(?ms).*/repair -> /usr/lib/snapd/snap-repair`) + + // run again and ensure no error happens + err = rpr.Run() + c.Assert(err, IsNil) + +} diff -Nru snapd-2.28.5/cmd/snap-repair/staging.go snapd-2.29.3/cmd/snap-repair/staging.go --- snapd-2.28.5/cmd/snap-repair/staging.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/staging.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,81 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- +// +build withtestkeys withstagingkeys + +/* + * Copyright (C) 2017 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 . + * + */ + +package main + +import ( + "fmt" + + "github.com/snapcore/snapd/asserts" + "github.com/snapcore/snapd/osutil" +) + +const ( + encodedStagingRepairRootAccountKey = `type: account-key +authority-id: canonical +public-key-sha3-384: 0GgXgD-RtfU0HJFBmaaiUQaNUFATl1oOlzJ44Bi4MbQAwcu8ektQLGBkKQ4JuA_O +account-id: canonical +name: repair-root +since: 2017-07-07T00:00:00.0Z +body-length: 1406 +sign-key-sha3-384: e2r8on4LgdxQSaW5T8mBD5oC2fSTktTVxOYa5w3kIP4_nNF6L7mt6fOJShdOGkKu + +AcbDTQRWhcGAASAA0D+AqdqBtgiaABSZ++NdDKcvFtpmEphDfpEAAo4MAWucmE5yVN9mHFRXvwU0 +ZkLQwPuiF5Vp5EP/kHyKgmmF9nKUBXnuZXfuH4vzH9ZuEfdxGc0On+XK4KwyPUzOXj2n1Rsxj0P5 +06wJ6QFghi6nORx3Hz4pxZH7SRANgZudwWE53+whbkJyU/psv4RXfxPnu3YGo0qPk/wGCfV8kkkH +UFmeqJJEk14EGI+Kv9WlIVAqctryqf9mSXkgnhp4lzdGpsRCmRcw7boVjdOieFCJv+gVs7i52Yxu +YyiA09XF8j85DSMl4s/TyN4bm9649beBCpTJNyb3Ep6lydGiZck8ChJRLDXGP2XZtRKXsBMNIfwP +5qnLtX1/Sm1znag0grGbd3AUqL6ySAr42x8QIxZfqzk5DvQbF3xOiu2xzxTt1eB3069MlnFw99ui +fLwlec7imbCiX3bryutCRhKgkJz4MbNsyHiW51k1l1IDbABey+gfVHpeBq/2aHK5qSy98dRBSYSj +Ki++j8zR1ODequWy+OrF4cu6IQ35eunHQ2mRsJIE0xFGAjG3vCPJwzVoNS5m5R0ffncIUYdKxt/R +W2mLo43qX0fquW5LvHyI14d3B3LYfKz05FmASJaE4A+/GQhM7kMCnmykro0MM6MU0sd2OruOZVVo +z6GQ37Hyo/TGToyCr7qD/+tSq3dKYGyezl/Y4I589eqnc1DaMHL2ssiXDsbpSRHpnNqMUq6UNg4M +NsUiDLaGsNJj1ft6A7jz+yoqJ74m3hlaQK1Rot8FXBkuJGoRKBahHbh/bfkGWChDvzY9ZpoUExY2 +rp7tNYEP/LEAI/RQd03sBnqd3V8YhggT2n6QCC4ikLKvUTE3RY0qn5aAa7KC1wVi+7SfdeVl3RBF +Jyb9GCYfRUv/bfFH/TCZ9WVN1v/GIMcjBFGJf7H3cz/deela53XSaecYHuvFRpVfzmx28UcR8UY4 +5WqHfxnVQlY6DPv+kjzMzIEJGwgSAFc0d4wlSwS/Y1T0ednFRUyjMAxEUvE8tOLibtXw4q/srIFt +OIgpd/xErcyi5Ddgt7EQoYo+rtVZ8x5EwR0+i7VAV+a3bnGSJW2LFEjt2RZUiMjohVZ4oOVuoDd2 +VQzMFv41flbyqjgHhtJSCIOKDg9uI2FHbQ5vrX9qBooS68YkBALwCq+P7nSxDxFuS0CgrzSH35FX +VneOl68U74pxRgdlPJ0HI92oilrbTH8Ft0m5SzNsy+9ZZZtIDFQW+lx/ApixyifARFnZ3C3Gdx59 +FlFNbE75+X28joGtul2mPjJ1eI1dCwiFCF3R/rwfRmw3Wpv76re+EzVR1MJVCcTgC1lUoCJpKl1J +n3PQLcR8J0iqswARAQAB + +AcLBXAQAAQoABgUCWYM7bQAKCRAHKljtl9kuLtCFD/4miBm0HyLE8GdboeUtWw+oOlH0AgabRqYi +a1TpEJeYQIjnwDuCCPYtJxL1Rc+5dSNnbY9L+34NuaSyYMJY/FMuSS5iaNomGnj7YiAOds1+1/6h +Z1bTm3ttZnphg5DxckYZLaKoYgRaOzAbiRM8l+2bDbXlq3KRxZ7o7D1V/xpPis8SWK57gQ7VppHI +fcw5jnzWokWSowaKShimjJNCXMaeGdGJBLU1wcJC/XRf3tXSZecwMfL9CN/G8b17HvIFN/Pe3oS9 +QxYMQ0p3J3PF3F19Iow0VHi78hPKtVmJb5igwzBlGYFW7zZ3R35nJ7Iv6VW58G2HDDGMdBfZp930 +FbLb3mj8Yw3S5fcMZ09vpT7PK0tjFoVJtDFBOkrjvxVMEPRa0IJNcfl/hgPdp1/IFXWpZhfvk8a8 +qgzffxN+Ro/J4Jt9QrHM4sNwiEOjVvHY4cQ9GOfns9UqocmxYPDxElBNraCFOCSudZgXiyF7zUYF +OnYqTDR4ChiZtmUqIiZr6rXgZTm1raGlqR7nsbDlkJtru7tzkgMRw8xFRolaQIKiyAwTewF7vLho +imwYTRuYRMzft1q5EeRWR4XwtlIuqsXg3FCGTNIG4HiAFKrrNV7AOvVjIUSgpOcWv2leSiRQjgpY +I9oD82ii+5rKvebnGIa0o+sWhYNFoviP/49DnDNJWA== +` +) + +func init() { + repairRootAccountKey, err := asserts.Decode([]byte(encodedStagingRepairRootAccountKey)) + if err != nil { + panic(fmt.Sprintf("cannot decode trusted account-key: %v", err)) + } + if osutil.GetenvBool("SNAPPY_USE_STAGING_STORE") { + trustedRepairRootKeys = append(trustedRepairRootKeys, repairRootAccountKey.(*asserts.AccountKey)) + } +} diff -Nru snapd-2.28.5/cmd/snap-repair/trace.go snapd-2.29.3/cmd/snap-repair/trace.go --- snapd-2.28.5/cmd/snap-repair/trace.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/trace.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,176 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + + "github.com/snapcore/snapd/dirs" +) + +// newRepairTraces returns all repairTrace about the given "brand" and "seq" +// that can be found. brand, seq can be filepath.Glob expressions. +func newRepairTraces(brand, seq string) ([]*repairTrace, error) { + matches, err := filepath.Glob(filepath.Join(dirs.SnapRepairRunDir, brand, seq, "*")) + if err != nil { + return nil, err + } + + var repairTraces []*repairTrace + for _, match := range matches { + if trace := newRepairTraceFromPath(match); trace != nil { + repairTraces = append(repairTraces, trace) + } + } + + return repairTraces, nil +} + +// repairTrace holds information about a repair that was run. +type repairTrace struct { + path string +} + +// validRepairTraceName checks that the given name looks like a valid repair +// trace +var validRepairTraceName = regexp.MustCompile(`^r[0-9]+\.(done|skip|retry|running)$`) + +// newRepairTraceFromPath takes a repair log path like +// the path /var/lib/snapd/repair/run/my-brand/1/r2.done +// and contructs a repair log from that. +func newRepairTraceFromPath(path string) *repairTrace { + rt := &repairTrace{path: path} + if !validRepairTraceName.MatchString(filepath.Base(path)) { + return nil + } + return rt +} + +// Repair returns the repair human readable string in the form $brand-$id +func (rt *repairTrace) Repair() string { + seq := filepath.Base(filepath.Dir(rt.path)) + brand := filepath.Base(filepath.Dir(filepath.Dir(rt.path))) + + return fmt.Sprintf("%s-%s", brand, seq) +} + +// Revision returns the revision of the repair +func (rt *repairTrace) Revision() string { + rev, err := revFromFilepath(rt.path) + if err != nil { + // this can never happen because we check that path starts + // with the right prefix. However handle the case just in + // case. + return "-" + } + return rev +} + +// Summary returns the summary of the repair that was run +func (rt *repairTrace) Summary() string { + f, err := os.Open(rt.path) + if err != nil { + return "-" + } + defer f.Close() + + needle := "summary: " + scanner := bufio.NewScanner(f) + for scanner.Scan() { + s := scanner.Text() + if strings.HasPrefix(s, needle) { + return s[len(needle):] + } + } + + return "-" +} + +// Status returns the status of the given repair {done,skip,retry,running} +func (rt *repairTrace) Status() string { + return filepath.Ext(rt.path)[1:] +} + +func indentPrefix(level int) string { + return strings.Repeat(" ", level) +} + +// WriteScriptIndented outputs the script that produced this repair output +// to the given writer w with the indent level given by indent. +func (rt *repairTrace) WriteScriptIndented(w io.Writer, indent int) error { + scriptPath := rt.path[:strings.LastIndex(rt.path, ".")] + ".script" + f, err := os.Open(scriptPath) + if err != nil { + return err + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + fmt.Fprintf(w, "%s%s\n", indentPrefix(indent), scanner.Text()) + } + if scanner.Err() != nil { + return scanner.Err() + } + return nil +} + +// WriteOutputIndented outputs the repair output to the given writer w +// with the indent level given by indent. +func (rt *repairTrace) WriteOutputIndented(w io.Writer, indent int) error { + f, err := os.Open(rt.path) + if err != nil { + return err + } + defer f.Close() + + scanner := bufio.NewScanner(f) + // move forward in the log to where the actual script output starts + for scanner.Scan() { + if scanner.Text() == "output:" { + break + } + } + // write the script output to w + for scanner.Scan() { + fmt.Fprintf(w, "%s%s\n", indentPrefix(indent), scanner.Text()) + } + if scanner.Err() != nil { + return scanner.Err() + } + return nil +} + +// revFromFilepath is a helper that extracts the revision number from the +// filename of the repairTrace +func revFromFilepath(name string) (string, error) { + var rev int + if _, err := fmt.Sscanf(filepath.Base(name), "r%d.", &rev); err == nil { + return strconv.Itoa(rev), nil + } + return "", fmt.Errorf("cannot find revision in %q", name) +} diff -Nru snapd-2.28.5/cmd/snap-repair/trace_test.go snapd-2.29.3/cmd/snap-repair/trace_test.go --- snapd-2.28.5/cmd/snap-repair/trace_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/trace_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,66 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main_test + +import ( + "io/ioutil" + "os" + "path/filepath" + + . "gopkg.in/check.v1" + + "github.com/snapcore/snapd/dirs" +) + +func makeMockRepairState(c *C) { + // the canonical script dir content + basedir := filepath.Join(dirs.SnapRepairRunDir, "canonical/1") + err := os.MkdirAll(basedir, 0700) + c.Assert(err, IsNil) + err = ioutil.WriteFile(filepath.Join(basedir, "r3.retry"), []byte("repair: canonical-1\nsummary: repair one\noutput:\nretry output"), 0600) + c.Assert(err, IsNil) + err = ioutil.WriteFile(filepath.Join(basedir, "r3.script"), []byte("#!/bin/sh\necho retry output"), 0700) + c.Assert(err, IsNil) + + // my-brand + basedir = filepath.Join(dirs.SnapRepairRunDir, "my-brand/1") + err = os.MkdirAll(basedir, 0700) + c.Assert(err, IsNil) + err = ioutil.WriteFile(filepath.Join(basedir, "r1.done"), []byte("repair: my-brand-1\nsummary: my-brand repair one\noutput:\ndone output"), 0600) + c.Assert(err, IsNil) + err = ioutil.WriteFile(filepath.Join(basedir, "r1.script"), []byte("#!/bin/sh\necho done output"), 0700) + c.Assert(err, IsNil) + + basedir = filepath.Join(dirs.SnapRepairRunDir, "my-brand/2") + err = os.MkdirAll(basedir, 0700) + c.Assert(err, IsNil) + err = ioutil.WriteFile(filepath.Join(basedir, "r2.skip"), []byte("repair: my-brand-2\nsummary: my-brand repair two\noutput:\nskip output"), 0600) + c.Assert(err, IsNil) + err = ioutil.WriteFile(filepath.Join(basedir, "r2.script"), []byte("#!/bin/sh\necho skip output"), 0700) + c.Assert(err, IsNil) + + basedir = filepath.Join(dirs.SnapRepairRunDir, "my-brand/3") + err = os.MkdirAll(basedir, 0700) + c.Assert(err, IsNil) + err = ioutil.WriteFile(filepath.Join(basedir, "r0.running"), []byte("repair: my-brand-3\nsummary: my-brand repair three\noutput:\nrunning output"), 0600) + c.Assert(err, IsNil) + err = ioutil.WriteFile(filepath.Join(basedir, "r0.script"), []byte("#!/bin/sh\necho running output"), 0700) + c.Assert(err, IsNil) +} diff -Nru snapd-2.28.5/cmd/snap-repair/trusted.go snapd-2.29.3/cmd/snap-repair/trusted.go --- snapd-2.28.5/cmd/snap-repair/trusted.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-repair/trusted.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,89 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main + +import ( + "fmt" + + "github.com/snapcore/snapd/asserts" + "github.com/snapcore/snapd/osutil" +) + +const ( + encodedRepairRootAccountKey = `type: account-key +authority-id: canonical +public-key-sha3-384: nttW6NfBXI_E-00u38W-KH6eiksfQNXuI7IiumoV49_zkbhM0sYTzSnFlwZC-W4t +account-id: canonical +name: repair-root +since: 2017-07-07T00:00:00.0Z +body-length: 1406 +sign-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk + +AcbDTQRWhcGAASAAtlCIEilQCQh9Ffjn9IxD+5FtWfdKdJHrQdtFPy/2Q1kOvC++ef/3bG1xMwao +tue9K0HCMtv3apHS1C32Y8JBMD8oykRpAd5H05+sgzZr3kCHIvgogKFsXfdd5+W5Q1+59Vy/81UH +tJCs99wBwboNh/pMCXBGI3jDRN1f7hOxcHUIW+KTaHCVZnrXXmCn6Oe6brR9qiUXgEB2I6rBT/Fe +cumdfvFN/zSsJ3Vvv9IbTfHYAZD82NrSqz4UZ3WJarIaxlykgLJaZN4bqSQYPYsc8lLlwjQeGloW ++r8dIypKOzPnUYurzWcNzcCnNCT1zhpY/IK2rFcZbN5/mP2/5PtjFlbX88aPGPbOTqYANmxfboCx +wo4D4aS7PD6gLC7XM8bgh8BACpmG3BnskL7F/9IHMl85SUHFIya2fDu7A7HqNUn7cpENGbHojj7G +J2s2965FSRuIvp69wEmknYD/kahjT1+Vy94D2rVB7mjtTruPueF2KTpo2jRXFM+ABq+T9ybjXD6f +UuSXu5xeg0Cv1sxOh4O4b45uaCXb8B74chEUW+cb3cV0NGE/QgBJUBeS68vUI8lqQFmPInci6Md4 +oiKFVbloL0ZmOGj73Xv2uAcexAK9bEiI+adVS2x9r4eFwtkST3XG0t/kw7eLgAVjtRcpmD6EuZ0Q +ulAJHEsl7Sazm8GRU4GtZWaCajVb4n5TS1lin2nqUXwqxRUA3smLqkQoYXQni9vmhez4dEP4MVvq +/0IdI50UGME5Fzc8hUhYzvbNS8g+VOeAK/qj3dzcdF9+n940AQI16Fcxi1/xFk8A4dw3AaDl4XnJ +piyStE+xi95ne5HJW8r/f/JQos8I6QR5w7pe2URbgUdVPgQLv3r/4dS/X3aP+oakrPR7JuAVdP62 +vsjF4jK8Bl69mcF434xpshnbnW/f7XHomPY4gp8y7kD2/DdEs5hvaTHIPp25DEYhqjt3gfmMuUXi +Mb5oy9KZp3ff8Squ+XNWSGQSyhX14xcQwM8QjNQnAisNg2hYwSM2n8q5IDWiwJQkFSriP5tMsa8E +DMGI3LXUZKRJll9dQBjs6VzApT4/Ee0ZvSni0d2cWm3wkqFQudRpqz3JSwQ7jal0W5e0UhNeHh/W +7nACD5hvcwF7UgUz0r8adlOy+nyfvWte65nbcRrIH7GS1xdgS0e9eW4znsplp7s/Z3gMhi8CN5TY +0nZW82TTl69Wvn13SGJye0yJSjiy4KS0iRE6BwAt7dGAMs5c62IlBsWEHLmCW1/lixWA9YXT9iji +G7DKSoofnsvqVP2wIQZxxt4xHMjUGXecyx8QX4BznwsV1vbzHOIG4a3Z9A1X1L3yh3ZbazFVeEE9 +7Dhz9hGYfd3PvwARAQAB + +AcLDXAQAAQoABgUCWbuO2gAKCRDUpVvql9g3IOPcIADZWObdYMKh2SblWXxUchnc3S4LDRtL8v+Q +HdnXO5+dJmsj5LWhdsB7lz373aaylFTwHpNDWcDdAu7ulP0vr7zJURo1jGOo7VojSEeuAAu3YhwL +2pR0p5Me0wuxl/pCX0x0nfDSeeTw11kproyN0GwJaErKEmyQyfOgVr2jN5sl1gBqQtKgG5gqZzC3 +oFH1HYGPl2kfAorxFw7MoPy4aRFaxUJfx4x6bEktgkkFT7AWGmawVwcpiiUbbpe9CPLEsn6yqJI9 +5XmQ3dJjp/6Y5D7x04LRH3Q5fajRcpdBrC0tDsT9UDbSRtIyo0KDNVHwQalLa2Sv51DV+Fy4eneM +Lgu+oCUOnBecXIWnX+k0uyDW8aLHYapx8etpW3pln/hMRd8JxYVYAqDn7G2AYeSGS/4lzCJzysW2 +2/4RhH9Ql8ea0nSWVTJr3pmXKlPSH/OOy9IADEDUuEdvyMcq3YOXA9E4L3g9vR31JH+++swcTQPz +rnGx0mE+TCQRWok/NZ1QNv4eNZlnLXdNS1DoV/kRqU04cblYYUoSO34mkjPEJ8ti+VzKh/PTA6/7 +1feFX276Zam/6b2rBLWCWYdblDM9oLAR4PfzntBZW4LzzOIb95IwiK4JoDoBr3x4+RxvxgVQLvHt +8990uQ0se9+8BtLVFtd07NbldHXRBlZkq22a8CeVYrU3YZEtuEBvlGDpkdegw/vcvgHUUK1f8dXJ +0+9oW2yQOLAguuPZ67GFSgdTnvR5dQYZZR2EbQJlPMOFA3loKeUxHSR9w7E3SFqXGqN1v6APDK0V +lpVFq+rYlprvbf4GB0oR8yQOGtlxf+Ag3Nnx+90nlmtTK4k7tQpLzuAXGGREDCtn0y/YvWvGt6kN +EV5Q/mAVe2/CtAUvfyX7V3xlzYCrJT9DBcCBMaUUekFrwvZi13WYJIn7YE2Qmam7ZsXdb991PoFv ++c6Pmeg6w3y7D+Vj4Yfi8IrjPrc6765DaaZxFyMia9GEQKHChZDkiEiAM6RfwlC5YXGzCroaZi0Y +Knf/UkUWoa/jKZgQNiqrZ9oGmbURLeXkkHzpcFitwjzWr6tNScCzNIqs/uxTxbFM8fJu1gSmauEY +TE1rn62SiuHNRKJqfLcCHucStK10knHkHTAJ3avS7rBz0Dy8UOa77bOjyei5n2rkyXztL2YjjGYh +8jEt00xcvwJGePBfH10gCgTFWdfhfcP9/muKgiOSErQlHPypnr4vqO0PU9XDp106FFWyyNPd95kC +l5IF9WMfl7YHpT0Ph7kBYwg9sKF/7oCVdbT5CoImxkE5DTkWB8xX6W/BhuMrp1rzTHFFGVd1ppb7 +EMUll4dd78OWonMlIgsMRuTSn93awb4X8xSJhRi9 +` +) + +func init() { + repairRootAccountKey, err := asserts.Decode([]byte(encodedRepairRootAccountKey)) + if err != nil { + panic(fmt.Sprintf("cannot decode trusted account-key: %v", err)) + } + if !osutil.GetenvBool("SNAPPY_USE_STAGING_STORE") { + trustedRepairRootKeys = append(trustedRepairRootKeys, repairRootAccountKey.(*asserts.AccountKey)) + } +} diff -Nru snapd-2.28.5/cmd/snap-seccomp/export_test.go snapd-2.29.3/cmd/snap-seccomp/export_test.go --- snapd-2.28.5/cmd/snap-seccomp/export_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap-seccomp/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -22,7 +22,6 @@ var ( Compile = compile SeccompResolver = seccompResolver - FindGid = findGid ) func MockArchUbuntuArchitecture(f func() string) (restore func()) { diff -Nru snapd-2.28.5/cmd/snap-seccomp/main.go snapd-2.29.3/cmd/snap-seccomp/main.go --- snapd-2.28.5/cmd/snap-seccomp/main.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap-seccomp/main.go 2017-10-27 12:31:06.000000000 +0000 @@ -28,7 +28,6 @@ //#include //#include //#include -//#include //#include //#include //#include @@ -136,11 +135,6 @@ // return htobe64(val); //} // -//static int mygetgrnam_r(const char *name, struct group *grp,char *buf, -// size_t buflen, struct group **result) { -// return getgrnam_r(name, grp, buf, buflen, result); -//} -// import "C" import ( @@ -149,13 +143,10 @@ "fmt" "io/ioutil" "os" - "os/user" - "path/filepath" "regexp" "strconv" "strings" "syscall" - "unsafe" // FIXME: we want github.com/seccomp/libseccomp-golang but that // will not work with trusty because libseccomp-golang checks @@ -467,137 +458,6 @@ return strconv.ParseUint(token, 10, 64) } -// Be very strict so usernames and groups specified in policy are widely -// compatible. From NAME_REGEX in /etc/adduser.conf -var userGroupNamePattern = regexp.MustCompile("^[a-z][-a-z0-9_]*$") - -func findUid(username string) (uint64, error) { - if !userGroupNamePattern.MatchString(username) { - return 0, fmt.Errorf("%q must be a valid username", username) - } - user, err := user.Lookup(username) - if err != nil { - return 0, err - } - - return strconv.ParseUint(user.Uid, 10, 64) -} - -// hrm, user.LookupGroup() doesn't exist yet: -// https://github.com/golang/go/issues/2617 -// -// Use implementation from upcoming releases: -// https://golang.org/src/os/user/lookup_unix.go -func lookupGroup(groupname string) (string, error) { - var grp C.struct_group - var result *C.struct_group - - buf := alloc(groupBuffer) - defer buf.free() - cname := C.CString(groupname) - defer C.free(unsafe.Pointer(cname)) - - err := retryWithBuffer(buf, func() syscall.Errno { - return syscall.Errno(C.mygetgrnam_r(cname, - &grp, - (*C.char)(buf.ptr), - C.size_t(buf.size), - &result)) - }) - if err != nil { - return "", fmt.Errorf("group: lookup groupname %s: %v", groupname, err) - } - if result == nil { - return "", fmt.Errorf("group: unknown group %s", groupname) - } - return strconv.Itoa(int(grp.gr_gid)), nil -} - -type bufferKind C.int - -const ( - groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX) -) - -func (k bufferKind) initialSize() C.size_t { - sz := C.sysconf(C.int(k)) - if sz == -1 { - // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX. - // Additionally, not all Linux systems have it, either. For - // example, the musl libc returns -1. - return 1024 - } - if !isSizeReasonable(int64(sz)) { - // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run. - return maxBufferSize - } - return C.size_t(sz) -} - -type memBuffer struct { - ptr unsafe.Pointer - size C.size_t -} - -func alloc(kind bufferKind) *memBuffer { - sz := kind.initialSize() - return &memBuffer{ - ptr: C.malloc(sz), - size: sz, - } -} - -func (mb *memBuffer) resize(newSize C.size_t) { - mb.ptr = C.realloc(mb.ptr, newSize) - mb.size = newSize -} - -func (mb *memBuffer) free() { - C.free(mb.ptr) -} - -// retryWithBuffer repeatedly calls f(), increasing the size of the -// buffer each time, until f succeeds, fails with a non-ERANGE error, -// or the buffer exceeds a reasonable limit. -func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error { - for { - errno := f() - if errno == 0 { - return nil - } else if errno != syscall.ERANGE { - return errno - } - newSize := buf.size * 2 - if !isSizeReasonable(int64(newSize)) { - return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize) - } - buf.resize(newSize) - } -} - -const maxBufferSize = 1 << 20 - -func isSizeReasonable(sz int64) bool { - return sz > 0 && sz <= maxBufferSize -} - -// end code from https://golang.org/src/os/user/lookup_unix.go - -func findGid(group string) (uint64, error) { - if !userGroupNamePattern.MatchString(group) { - return 0, fmt.Errorf("%q must be a valid group name", group) - } - - //group, err := user.LookupGroup(group) - group, err := lookupGroup(group) - if err != nil { - return 0, err - } - - //return strconv.ParseUint(group.Gid, 10, 64) - return strconv.ParseUint(group, 10, 64) -} - func parseLine(line string, secFilter *seccomp.ScmpFilter) error { // ignore comments and empty lines if strings.HasPrefix(line, "#") || line == "" { @@ -759,13 +619,14 @@ for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) - // FIXME: right now complain mode is the equivalent to - // unrestricted. We'll want to change this once we - // seccomp logging is in order. - // - // special case: unrestricted means we switch to an allow-all - // filter and are done - if line == "@unrestricted" || line == "@complain" { + // special case: unrestricted means we stop early, we just + // write this special tag and evalulate in snap-confine + if line == "@unrestricted" { + return osutil.AtomicWrite(out, bytes.NewBufferString(line+"\n"), 0644, 0) + } + // complain mode is a "allow-all" filter for now until + // we can land https://github.com/snapcore/snapd/pull/3998 + if line == "@complain" { secFilter, err = seccomp.NewFilter(seccomp.ActAllow) if err != nil { return fmt.Errorf("cannot create seccomp filter: %s", err) @@ -790,27 +651,36 @@ } // write atomically - dir, err := os.Open(filepath.Dir(out)) - if err != nil { - return err - } - defer dir.Close() - - fout, err := os.Create(out + ".tmp") + fout, err := osutil.NewAtomicFile(out, 0644, 0, -1, -1) if err != nil { return err } defer fout.Close() - if err := secFilter.ExportBPF(fout); err != nil { + + if err := secFilter.ExportBPF(fout.File); err != nil { return err } - if err := fout.Sync(); err != nil { - return err + return fout.Commit() +} + +// Be very strict so usernames and groups specified in policy are widely +// compatible. From NAME_REGEX in /etc/adduser.conf +var userGroupNamePattern = regexp.MustCompile("^[a-z][-a-z0-9_]*$") + +// findUid returns the identifier of the given UNIX user name. +func findUid(username string) (uint64, error) { + if !userGroupNamePattern.MatchString(username) { + return 0, fmt.Errorf("%q must be a valid username", username) } - if err := os.Rename(out+".tmp", out); err != nil { - return err + return osutil.FindUid(username) +} + +// findGid returns the identifier of the given UNIX group name. +func findGid(group string) (uint64, error) { + if !userGroupNamePattern.MatchString(group) { + return 0, fmt.Errorf("%q must be a valid group name", group) } - return dir.Sync() + return osutil.FindGid(group) } func showSeccompLibraryVersion() error { diff -Nru snapd-2.28.5/cmd/snap-seccomp/main_test.go snapd-2.29.3/cmd/snap-seccomp/main_test.go --- snapd-2.28.5/cmd/snap-seccomp/main_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/cmd/snap-seccomp/main_test.go 2017-10-30 18:11:24.000000000 +0000 @@ -36,6 +36,8 @@ "github.com/snapcore/snapd/arch" main "github.com/snapcore/snapd/cmd/snap-seccomp" + "github.com/snapcore/snapd/osutil" + "github.com/snapcore/snapd/release" ) // Hook up check.v1 into the "go test" runner @@ -164,11 +166,28 @@ s.seccompSyscallRunner = filepath.Join(c.MkDir(), "seccomp_syscall_runner") err = ioutil.WriteFile(s.seccompSyscallRunner+".c", seccompSyscallRunnerContent, 0644) c.Assert(err, IsNil) + cmd = exec.Command("gcc", "-std=c99", "-Werror", "-Wall", "-static", s.seccompSyscallRunner+".c", "-o", s.seccompSyscallRunner, "-Wl,-static", "-static-libgcc") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Run() c.Assert(err, IsNil) + + // Build 32bit runner on amd64 to test non-native syscall handling. + // Ideally we would build for ppc64el->powerpc and arm64->armhf but + // it seems tricky to find the right gcc-multilib for this. + if arch.UbuntuArchitecture() == "amd64" { + cmd = exec.Command(cmd.Args[0], cmd.Args[1:]...) + cmd.Args = append(cmd.Args, "-m32") + for i, k := range cmd.Args { + if k == s.seccompSyscallRunner { + cmd.Args[i] = s.seccompSyscallRunner + ".m32" + } + } + if output, err := cmd.CombinedOutput(); err != nil { + fmt.Printf("cannot build multi-lib syscall runner: %v\n%s", err, output) + } + } } // Runs the policy through the kernel: @@ -190,10 +209,6 @@ // sync_file_range, and truncate64. // Once we start using those. See `man syscall` func (s *snapSeccompSuite) runBpf(c *C, seccompWhitelist, bpfInput string, expected int) { - outPath := filepath.Join(c.MkDir(), "bpf") - err := main.Compile([]byte(seccompWhitelist), outPath) - c.Assert(err, IsNil) - // Common syscalls we need to allow for a minimal statically linked // c program. // @@ -217,26 +232,63 @@ # arm64 readlinkat faccessat +# i386 from amd64 +restart_syscall ` bpfPath := filepath.Join(c.MkDir(), "bpf") - err = main.Compile([]byte(common+seccompWhitelist), bpfPath) + err := main.Compile([]byte(common+seccompWhitelist), bpfPath) c.Assert(err, IsNil) + // default syscall runner + syscallRunner := s.seccompSyscallRunner + // syscallName;arch;arg1,arg2... l := strings.Split(bpfInput, ";") - if len(l) > 1 && l[1] != "native" { - c.Logf("cannot use non-native in runBpfInKernel") - return + syscallName := l[0] + syscallArch := "native" + if len(l) > 1 { + syscallArch = l[1] } - var syscallRunnerArgs [7]string - syscallNr, err := seccomp.GetSyscallFromName(l[0]) + syscallNr, err := seccomp.GetSyscallFromName(syscallName) c.Assert(err, IsNil) - if syscallNr < 0 { - c.Skip(fmt.Sprintf("skipping %v because it resolves to negative %v", l[0], syscallNr)) + + // Check if we want to test non-native architecture + // handling. Doing this via the in-kernel tests is tricky as + // we need a kernel that can run the architecture and a + // compiler that can produce the required binaries. Currently + // we only test amd64 running i386 here. + if syscallArch != "native" { + syscallNr, err = seccomp.GetSyscallFromNameByArch(syscallName, main.UbuntuArchToScmpArch(syscallArch)) + c.Assert(err, IsNil) + + switch syscallArch { + case "amd64": + // default syscallRunner + case "i386": + syscallRunner = s.seccompSyscallRunner + ".m32" + default: + c.Errorf("unexpected non-native arch: %s", syscallArch) + } + } + switch { + case syscallNr == -101: + // "socket" + // see libseccomp: _s390x_sock_demux(), _x86_sock_demux() + // the -101 is translated to 359 (socket) + syscallNr = 359 + case syscallNr == -10165: + // "mknod" on arm64 is not available at all on arm64 + // only "mknodat" but libseccomp will not generate a + // "mknodat" whitelist, it geneates a whitelist with + // syscall -10165 (!?!) so we cannot test this. + c.Skip("skipping mknod tests on arm64") + case syscallNr < 0: + c.Errorf("failed to resolve %v: %v", l[0], syscallNr) return } + var syscallRunnerArgs [7]string syscallRunnerArgs[0] = strconv.FormatInt(int64(syscallNr), 10) if len(l) > 2 { args := strings.Split(l[2], ",") @@ -253,7 +305,7 @@ } } - cmd := exec.Command(s.seccompBpfLoader, bpfPath, s.seccompSyscallRunner, syscallRunnerArgs[0], syscallRunnerArgs[1], syscallRunnerArgs[2], syscallRunnerArgs[3], syscallRunnerArgs[4], syscallRunnerArgs[5], syscallRunnerArgs[6]) + cmd := exec.Command(s.seccompBpfLoader, bpfPath, syscallRunner, syscallRunnerArgs[0], syscallRunnerArgs[1], syscallRunnerArgs[2], syscallRunnerArgs[3], syscallRunnerArgs[4], syscallRunnerArgs[5], syscallRunnerArgs[6]) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -272,6 +324,18 @@ } } +func (s *snapSeccompSuite) TestUnrestricted(c *C) { + inp := "@unrestricted\n" + outPath := filepath.Join(c.MkDir(), "bpf") + err := main.Compile([]byte(inp), outPath) + c.Assert(err, IsNil) + + content, err := ioutil.ReadFile(outPath) + c.Assert(err, IsNil) + c.Check(content, DeepEquals, []byte(inp)) + +} + // TestCompile iterates over a range of textual seccomp whitelist rules and // mocked kernel syscall input. For each rule, the test consists of compiling // the rule into a bpf program and then running that program on a virtual bpf @@ -287,7 +351,7 @@ // {"read >=2", "read;native;0", main.SeccompRetKill}, func (s *snapSeccompSuite) TestCompile(c *C) { // The 'shadow' group is different in different distributions - shadowGid, err := main.FindGid("shadow") + shadowGid, err := osutil.FindGid("shadow") c.Assert(err, IsNil) for _, t := range []struct { @@ -296,7 +360,6 @@ expected int }{ // special - {"@unrestricted", "execve", main.SeccompRetAllow}, {"@complain", "execve", main.SeccompRetAllow}, // trivial allow @@ -400,6 +463,10 @@ // Some architectures (i386, s390x) use the "socketcall" syscall instead // of "socket". This is the case on Ubuntu 14.04, 17.04, 17.10 func (s *snapSeccompSuite) TestCompileSocket(c *C) { + if release.ReleaseInfo.ID == "ubuntu" && release.ReleaseInfo.VersionID == "14.04" { + c.Skip("14.04/i386 uses socketcall which cannot be tested here") + } + for _, t := range []struct { seccompWhitelist string bpfInput string @@ -512,6 +579,10 @@ // ported from test_restrictions_working_args_socket func (s *snapSeccompSuite) TestRestrictionsWorkingArgsSocket(c *C) { + if release.ReleaseInfo.ID == "ubuntu" && release.ReleaseInfo.VersionID == "14.04" { + c.Skip("14.04/i386 uses socketcall which cannot be tested here") + } + for _, pre := range []string{"AF", "PF"} { for _, i := range []string{"UNIX", "LOCAL", "INET", "INET6", "IPX", "NETLINK", "X25", "AX25", "ATMPVC", "APPLETALK", "PACKET", "ALG", "CAN", "BRIDGE", "NETROM", "ROSE", "NETBEUI", "SECURITY", "KEY", "ASH", "ECONET", "SNA", "IRDA", "PPPOX", "WANPIPE", "BLUETOOTH", "RDS", "LLC", "TIPC", "IUCV", "RXRPC", "ISDN", "PHONET", "IEEE802154", "CAIF", "NFC", "VSOCK", "MPLS", "IB"} { seccompWhitelist := fmt.Sprintf("socket %s_%s", pre, i) @@ -701,12 +772,6 @@ // on amd64 we add compat i386 {"amd64", "read", "read;i386", main.SeccompRetAllow}, {"amd64", "read", "read;amd64", main.SeccompRetAllow}, - // on arm64 we add compat armhf - {"arm64", "read", "read;armhf", main.SeccompRetAllow}, - {"arm64", "read", "read;arm64", main.SeccompRetAllow}, - // on ppc64 we add compat powerpc - {"ppc64", "read", "read;powerpc", main.SeccompRetAllow}, - {"ppc64", "read", "read;ppc64", main.SeccompRetAllow}, } { // It is tricky to mock the architecture here because // seccomp is always adding the native arch to the seccomp diff -Nru snapd-2.28.5/cmd/snap-update-ns/bootstrap.c snapd-2.29.3/cmd/snap-update-ns/bootstrap.c --- snapd-2.28.5/cmd/snap-update-ns/bootstrap.c 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/cmd/snap-update-ns/bootstrap.c 2017-10-23 06:17:27.000000000 +0000 @@ -15,12 +15,20 @@ * */ +// IMPORTANT: all the code in this file may be run with elevated privileges +// when invoking snap-update-ns from the setuid snap-confine. +// +// This file is a preprocessor for snap-update-ns' main() function. It will +// perform input validation and clear the environment so that snap-update-ns' +// go code runs with safe inputs when called by the setuid() snap-confine. + #include "bootstrap.h" #include #include #include #include +#include #include #include #include @@ -43,12 +51,21 @@ bootstrap_msg = "cannot open /proc/self/cmdline"; return -1; } - memset(buf, 0, buf_size); + // Ensure buf is initialized to all NULLs since our parsing of cmdline with + // its embedded NULLs depends on this. Use for loop instead of memset() to + // guarantee this initialization won't be optimized away. + size_t i; + for (i = 0; i < buf_size; ++i) { + buf[i] = '\0'; + } ssize_t num_read = read(fd, buf, buf_size); if (num_read < 0) { bootstrap_errno = errno; bootstrap_msg = "cannot read /proc/self/cmdline"; - } else if (num_read == buf_size) { + } else if (num_read == 0) { + bootstrap_errno = errno; + bootstrap_msg = "unexpectedly /proc/self/cmdline is empty"; + } else if (num_read == buf_size && buf_size > 0 && buf[buf_size - 1] != '\0') { bootstrap_errno = 0; bootstrap_msg = "cannot fit all of /proc/self/cmdline, buffer too small"; num_read = -1; @@ -57,36 +74,35 @@ return num_read; } -// find_argv0 scans the command line buffer and looks for the 0st argument. +// find_snap_name scans the command line buffer and looks for the 1st argument. +// if the 1st argument exists but is empty NULL is returned. const char* -find_argv0(char* buf, size_t num_read) +find_snap_name(const char* buf) { - // cmdline is an array of NUL ('\0') separated strings. - size_t argv0_len = strnlen(buf, num_read); - if (argv0_len == num_read) { - // ensure that the buffer is properly terminated. + // Skip the zeroth argument as well as any options. We know buf is NUL + // padded and terminated from read_cmdline(). + do { + size_t arg_len = strlen(buf); + buf += arg_len + 1; + } while (buf[0] == '-'); + + const char* snap_name = buf; + if (strlen(snap_name) == 0) { return NULL; } - return buf; + return snap_name; } -// find_snap_name scans the command line buffer and looks for the 1st argument. -// if the 1st argument exists but is empty NULL is returned. const char* -find_snap_name(char* buf, size_t num_read) +find_1st_option(const char* buf) { - // cmdline is an array of NUL ('\0') separated strings. We can skip over - // the first entry (program name) and look at the second entry, in our case - // it should be the snap name. - size_t argv0_len = strnlen(buf, num_read); - if (argv0_len + 1 >= num_read) { - return NULL; - } - char* snap_name = &buf[argv0_len + 1]; - if (*snap_name == '\0') { - return NULL; + // Skip the zeroth argument and find the first command line option. + // We know buf is NUL padded and terminated from read_cmdline(). + size_t pos = strlen(buf) + 1; + if (buf[pos] == '-') { + return &buf[pos]; } - return snap_name; + return NULL; } // setns_into_snap switches mount namespace into that of a given snap. @@ -94,17 +110,18 @@ setns_into_snap(const char* snap_name) { // Construct the name of the .mnt file to open. - char buf[PATH_MAX]; + char buf[PATH_MAX] = { + 0, + }; int n = snprintf(buf, sizeof buf, "/run/snapd/ns/%s.mnt", snap_name); if (n >= sizeof buf || n < 0) { - bootstrap_errno = errno; + bootstrap_errno = 0; bootstrap_msg = "cannot format mount namespace file name"; return -1; } - // Open the mount namespace file, note that we don't specify O_NOFOLLOW as - // that file is always a special, broken symbolic link. - int fd = open(buf, O_RDONLY | O_CLOEXEC); + // Open the mount namespace file. + int fd = open(buf, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); if (fd < 0) { bootstrap_errno = errno; bootstrap_msg = "cannot open mount namespace file"; @@ -122,37 +139,105 @@ return err; } -// partially_validate_snap_name performs partial validation of the given name. -// The goal is to ensure that there are no / or .. in the name. -int partially_validate_snap_name(const char* snap_name) +// TODO: reuse the code from snap-confine, if possible. +static int skip_lowercase_letters(const char** p) { - // NOTE: neither set bootstrap_{msg,errno} but the return value means that - // bootstrap does nothing. The name is re-validated by golang. - if (strstr(snap_name, "..") != NULL) { - return -1; + int skipped = 0; + const char* c; + for (c = *p; *c >= 'a' && *c <= 'z'; ++c) { + skipped += 1; } - if (strchr(snap_name, '/') != NULL) { - return -1; + *p = (*p) + skipped; + return skipped; +} + +// TODO: reuse the code from snap-confine, if possible. +static int skip_digits(const char** p) +{ + int skipped = 0; + const char* c; + for (c = *p; *c >= '0' && *c <= '9'; ++c) { + skipped += 1; } + *p = (*p) + skipped; + return skipped; } -// bootstrap prepares snap-update-ns to work in the namespace of the snap given -// on command line. -void bootstrap(void) +// TODO: reuse the code from snap-confine, if possible. +static int skip_one_char(const char** p, char c) { - // We don't have argc/argv so let's imitate that by reading cmdline - char cmdline[1024]; - memset(cmdline, 0, sizeof cmdline); - ssize_t num_read; - if ((num_read = read_cmdline(cmdline, sizeof cmdline)) < 0) { - return; + if (**p == c) { + *p += 1; + return 1; + } + return 0; +} + +// validate_snap_name performs full validation of the given name. +int validate_snap_name(const char* snap_name) +{ + // NOTE: This function should be synchronized with the two other + // implementations: sc_snap_name_validate and snap.ValidateName. + + // Ensure that name is not NULL + if (snap_name == NULL) { + bootstrap_msg = "snap name cannot be NULL"; + return -1; + } + // This is a regexp-free routine hand-codes the following pattern: + // + // "^([a-z0-9]+-?)*[a-z](-?[a-z0-9])*$" + // + // The only motivation for not using regular expressions is so that we + // don't run untrusted input against a potentially complex regular + // expression engine. + const char* p = snap_name; + if (skip_one_char(&p, '-')) { + bootstrap_msg = "snap name cannot start with a dash"; + return -1; + } + bool got_letter = false; + for (; *p != '\0';) { + if (skip_lowercase_letters(&p) > 0) { + got_letter = true; + continue; + } + if (skip_digits(&p) > 0) { + continue; + } + if (skip_one_char(&p, '-') > 0) { + if (*p == '\0') { + bootstrap_msg = "snap name cannot end with a dash"; + return -1; + } + if (skip_one_char(&p, '-') > 0) { + bootstrap_msg = "snap name cannot contain two consecutive dashes"; + return -1; + } + continue; + } + bootstrap_msg = "snap name must use lower case letters, digits or dashes"; + return -1; + } + if (!got_letter) { + bootstrap_msg = "snap name must contain at least one letter"; + return -1; } - // Find the name of the called program. If it is ending with "-test" then do nothing. + bootstrap_msg = NULL; + return 0; +} + +// process_arguments parses given cmdline which must be list of strings separated with NUL bytes. +// cmdline is an array of NUL ('\0') separated strings and guaranteed to be +// NUL-terminated via read_cmdline(). +void process_arguments(const char* cmdline, const char** snap_name_out, bool* should_setns_out) +{ + // Find the name of the called program. If it is ending with ".test" then do nothing. // NOTE: This lets us use cgo/go to write tests without running the bulk - // of the code automatically. In snapd we can just set the required - // environment variable. - const char* argv0 = find_argv0(cmdline, (size_t)num_read); + // of the code automatically. + // + const char* argv0 = cmdline; if (argv0 == NULL) { bootstrap_errno = 0; bootstrap_msg = "argv0 is corrupted"; @@ -165,23 +250,78 @@ return; } + // When we are running under "--from-snap-confine" option skip the setns + // call as snap-confine has already placed us in the right namespace. + const char* option = find_1st_option(cmdline); + bool should_setns = true; + if (option != NULL) { + if (strncmp(option, "--from-snap-confine", strlen("--from-snap-confine")) == 0) { + should_setns = false; + } else { + bootstrap_errno = 0; + bootstrap_msg = "unsupported option"; + return; + } + } + // Find the name of the snap by scanning the cmdline. If there's no snap // name given, just bail out. The go parts will scan this too. - const char* snap_name = find_snap_name(cmdline, (size_t)num_read); + const char* snap_name = find_snap_name(cmdline); if (snap_name == NULL) { + bootstrap_errno = 0; + bootstrap_msg = "snap name not provided"; return; } - // Look for known offenders in the snap name (.. and /) and do nothing if - // those are found. The golang code will validate snap name and print a - // proper error message but this just ensures we don't try to open / setns - // anything unusual. - if (partially_validate_snap_name(snap_name) < 0) { + // Ensure that the snap name is valid so that we don't blindly setns into + // something that is controlled by a potential attacker. + if (validate_snap_name(snap_name) < 0) { + bootstrap_errno = 0; + // bootstap_msg is set by validate_snap_name; return; } + // We have a valid snap name now so let's store it. + if (snap_name_out != NULL) { + *snap_name_out = snap_name; + } + if (should_setns_out != NULL) { + *should_setns_out = should_setns; + } + bootstrap_errno = 0; + bootstrap_msg = NULL; +} - // Switch the mount namespace to that of the snap given on command line. - if (setns_into_snap(snap_name) < 0) { +// bootstrap prepares snap-update-ns to work in the namespace of the snap given +// on command line. +void bootstrap(void) +{ + // We may have been started via a setuid-root snap-confine. In order to + // prevent environment-based attacks we start by erasing all environment + // variables. + if (clearenv() != 0) { + bootstrap_errno = 0; + bootstrap_msg = "bootstrap could not clear the environment"; return; } + // We don't have argc/argv so let's imitate that by reading cmdline + // NOTE: use explicit initialization to avoid optimizing-out memset. + char cmdline[1024] = { + 0, + }; + ssize_t num_read; + if ((num_read = read_cmdline(cmdline, sizeof cmdline)) < 0) { + // read_cmdline sets bootstrap_{errno,msg} + return; + } + + // Analyze the read process cmdline to find the snap name and decide if we + // should use setns to jump into the mount namespace of a particular snap. + // This is spread out for easier testability. + const char* snap_name = NULL; + bool should_setns = false; + process_arguments(cmdline, &snap_name, &should_setns); + if (snap_name != NULL && should_setns) { + setns_into_snap(snap_name); + // setns_into_snap sets bootstrap_{errno,msg} + } } diff -Nru snapd-2.28.5/cmd/snap-update-ns/bootstrap.go snapd-2.29.3/cmd/snap-update-ns/bootstrap.go --- snapd-2.28.5/cmd/snap-update-ns/bootstrap.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/cmd/snap-update-ns/bootstrap.go 2017-10-23 06:17:27.000000000 +0000 @@ -48,6 +48,9 @@ ErrNoNamespace = errors.New("cannot update mount namespace that was not created yet") ) +// IMPORTANT: all the code in this section may be run with elevated privileges +// when invoking snap-update-ns from the setuid snap-confine. + // BootstrapError returns error (if any) encountered in pre-main C code. func BootstrapError() error { if C.bootstrap_msg == nil { @@ -64,32 +67,53 @@ return fmt.Errorf("%s", C.GoString(C.bootstrap_msg)) } +// END IMPORTANT + // readCmdline is a wrapper around the C function read_cmdline. func readCmdline(buf []byte) C.ssize_t { return C.read_cmdline((*C.char)(unsafe.Pointer(&buf[0])), C.size_t(cap(buf))) } -// findArgv0 parses the argv-like array and finds the 0st argument. -func findArgv0(buf []byte) *string { - if ptr := C.find_argv0((*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf))); ptr != nil { +// findSnapName parses the argv-like array and finds the 1st argument. +func findSnapName(buf []byte) *string { + if ptr := C.find_snap_name((*C.char)(unsafe.Pointer(&buf[0]))); ptr != nil { str := C.GoString(ptr) return &str } return nil } -// findSnapName parses the argv-like array and finds the 1st argument. -func findSnapName(buf []byte) *string { - if ptr := C.find_snap_name((*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf))); ptr != nil { +// findFirstOption returns the first "-option" string in argv-like array. +func findFirstOption(buf []byte) *string { + if ptr := C.find_1st_option((*C.char)(unsafe.Pointer(&buf[0]))); ptr != nil { str := C.GoString(ptr) return &str } return nil } -// partiallyValidateSnapName checks if snap name is seemingly valid. -// The real part of the validation happens on the go side. -func partiallyValidateSnapName(snapName string) int { +// validateSnapName checks if snap name is valid. +// This also sets bootstrap_msg on failure. +func validateSnapName(snapName string) int { cStr := C.CString(snapName) - return int(C.partially_validate_snap_name(cStr)) + return int(C.validate_snap_name(cStr)) +} + +// processArguments parses commnad line arguments. +// The argument cmdline is a string with embedded +// NUL bytes, separating particular arguments. +func processArguments(cmdline []byte) (snapName string, shouldSetNs bool) { + var snapNameOut *C.char + var shouldSetNsOut C.bool + var buf *C.char + if len(cmdline) > 0 { + buf = (*C.char)(unsafe.Pointer(&cmdline[0])) + } + C.process_arguments(buf, &snapNameOut, &shouldSetNsOut) + if snapNameOut != nil { + snapName = C.GoString(snapNameOut) + } + shouldSetNs = bool(shouldSetNsOut) + + return snapName, shouldSetNs } diff -Nru snapd-2.28.5/cmd/snap-update-ns/bootstrap.h snapd-2.29.3/cmd/snap-update-ns/bootstrap.h --- snapd-2.28.5/cmd/snap-update-ns/bootstrap.h 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/cmd/snap-update-ns/bootstrap.h 2017-10-23 06:17:27.000000000 +0000 @@ -20,15 +20,18 @@ #define _GNU_SOURCE +#include #include extern int bootstrap_errno; extern const char* bootstrap_msg; void bootstrap(void); +void process_arguments(const char* cmdline, const char** snap_name_out, bool* should_setns_out); ssize_t read_cmdline(char* buf, size_t buf_size); -const char* find_argv0(char* buf, size_t num_read); -const char* find_snap_name(char* buf, size_t num_read); -int partially_validate_snap_name(const char* snap_name); +const char* find_argv0(const char* buf); +const char* find_snap_name(const char* buf); +const char* find_1st_option(const char* buf); +int validate_snap_name(const char* snap_name); #endif diff -Nru snapd-2.28.5/cmd/snap-update-ns/bootstrap_test.go snapd-2.29.3/cmd/snap-update-ns/bootstrap_test.go --- snapd-2.28.5/cmd/snap-update-ns/bootstrap_test.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/cmd/snap-update-ns/bootstrap_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -80,29 +80,94 @@ c.Assert(result, Equals, (*string)(nil)) } -// Check that if argv0 is returned as expected -func (s *bootstrapSuite) TestFindArgv0(c *C) { - buf := []byte("arg0\x00argv1\x00") - result := update.FindArgv0(buf) +// Check that if the 1st argument is an option then it is skipped. +func (s *bootstrapSuite) TestFindSnapName6(c *C) { + buf := []byte("arg0\x00--option\x00snap\x00\x00") + result := update.FindSnapName(buf) c.Assert(result, NotNil) - c.Assert(*result, Equals, "arg0") + c.Assert(*result, Equals, "snap") } -// Check that if argv0 is unterminated we return NULL. -func (s *bootstrapSuite) TestFindArgv0Unterminated(c *C) { - buf := []byte("arg0") - result := update.FindArgv0(buf) - c.Assert(result, Equals, (*string)(nil)) +// Check that if many options are skipped. +func (s *bootstrapSuite) TestFindSnapName7(c *C) { + buf := []byte("arg0\x00--option\x00--another\x00snap\x00\x00") + result := update.FindSnapName(buf) + c.Assert(result, NotNil) + c.Assert(*result, Equals, "snap") +} + +// Check that if there are no options we just return nil. +func (s *bootstrapSuite) TestFindFirstOption1(c *C) { + for _, str := range []string{"\x00", "arg0\x00", "arg0\x00arg1\x00"} { + result := update.FindFirstOption([]byte(str)) + c.Assert(result, Equals, (*string)(nil)) + } +} + +// Check that if there are are options we return the first one. +func (s *bootstrapSuite) TestFindFirstOption2(c *C) { + expected := "-o1" + for _, str := range []string{ + "\x00-o1\x00", + "arg0\x00-o1\x00", + "arg0\x00-o1\x00arg1\x00", + "arg0\x00-o1\x00-o2\x00", + "arg0\x00-o1\x00-o2\x00arg1\x00", + } { + result := update.FindFirstOption([]byte(str)) + c.Assert(result, DeepEquals, &expected, Commentf("got %q", *result)) + } } -// Check that PartiallyValidateSnapName rejects "/" and "..". -func (s *bootstrapSuite) TestPartiallyValidateSnapName(c *C) { - c.Assert(update.PartiallyValidateSnapName("hello-world"), Equals, 0) - c.Assert(update.PartiallyValidateSnapName("hello/world"), Equals, -1) - c.Assert(update.PartiallyValidateSnapName("hello..world"), Equals, -1) +// Check that ValidateSnapName rejects "/" and "..". +func (s *bootstrapSuite) TestValidateSnapName(c *C) { + c.Assert(update.ValidateSnapName("hello-world"), Equals, 0) + c.Assert(update.ValidateSnapName("hello/world"), Equals, -1) + c.Assert(update.ValidateSnapName("hello..world"), Equals, -1) + c.Assert(update.ValidateSnapName("INVALID"), Equals, -1) + c.Assert(update.ValidateSnapName("-invalid"), Equals, -1) } -// Check that pre-go bootstrap code is disabled while testing. -func (s *bootstrapSuite) TestBootstrapDisabled(c *C) { - c.Assert(update.BootstrapError(), ErrorMatches, "bootstrap is not enabled while testing") +// Test various cases of command line handling. +func (s *bootstrapSuite) TestProcessArguments(c *C) { + cases := []struct { + cmdline string + snapName string + shouldSetNs bool + errPattern string + }{ + // Corrupted buffer is dealt with. + {"", "", false, "argv0 is corrupted"}, + // When testing real bootstrap is identified and disabled. + {"argv0.test\x00", "", false, "bootstrap is not enabled while testing"}, + // Snap name is mandatory. + {"argv0\x00", "", false, "snap name not provided"}, + {"argv0\x00\x00", "", false, "snap name not provided"}, + // Snap name is parsed correctly. + {"argv0\x00snapname\x00", "snapname", true, ""}, + // Snap name is validated correctly. + {"argv0\x00in--valid\x00", "", false, "snap name cannot contain two consecutive dashes"}, + {"argv0\x00invalid-\x00", "", false, "snap name cannot end with a dash"}, + {"argv0\x00@invalid\x00", "", false, "snap name must use lower case letters, digits or dashes"}, + {"argv0\x00INVALID\x00", "", false, "snap name must use lower case letters, digits or dashes"}, + // The option --from-snap-confine disables setns. + {"argv0\x00--from-snap-confine\x00snapname\x00", "snapname", false, ""}, + // Unknown options are reported. + {"argv0\x00-invalid\x00", "", false, "unsupported option"}, + {"argv0\x00--option\x00", "", false, "unsupported option"}, + } + for _, tc := range cases { + buf := []byte(tc.cmdline) + snapName, shouldSetNs := update.ProcessArguments(buf) + err := update.BootstrapError() + comment := Commentf("failed with cmdline %q, expected error pattern %q, actual error %q", + tc.cmdline, tc.errPattern, err) + if tc.errPattern != "" { + c.Assert(err, ErrorMatches, tc.errPattern, comment) + } else { + c.Assert(err, IsNil, comment) + } + c.Check(snapName, Equals, tc.snapName) + c.Check(shouldSetNs, Equals, tc.shouldSetNs) + } } diff -Nru snapd-2.28.5/cmd/snap-update-ns/change.go snapd-2.29.3/cmd/snap-update-ns/change.go --- snapd-2.28.5/cmd/snap-update-ns/change.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-update-ns/change.go 2017-11-03 16:15:11.000000000 +0000 @@ -0,0 +1,153 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main + +import ( + "fmt" + "path" + "sort" + "strings" + "syscall" + + "github.com/snapcore/snapd/interfaces/mount" +) + +// Action represents a mount action (mount, remount, unmount, etc). +type Action string + +const ( + // Keep indicates that a given mount entry should be kept as-is. + Keep Action = "keep" + // Mount represents an action that results in mounting something somewhere. + Mount Action = "mount" + // Unmount represents an action that results in unmounting something from somewhere. + Unmount Action = "unmount" + // Remount when needed +) + +// Change describes a change to the mount table (action and the entry to act on). +type Change struct { + Entry mount.Entry + Action Action +} + +// String formats mount change to a human-readable line. +func (c Change) String() string { + return fmt.Sprintf("%s (%s)", c.Action, c.Entry) +} + +var ( + sysMount = syscall.Mount + sysUnmount = syscall.Unmount +) + +const unmountNoFollow = 8 + +// Perform executes the desired mount or unmount change using system calls. +// Filesystems that depend on helper programs or multiple independent calls to +// the kernel (--make-shared, for example) are unsupported. +func (c *Change) Perform() error { + switch c.Action { + case Mount: + flags, err := mount.OptsToFlags(c.Entry.Options) + if err != nil { + return err + } + return sysMount(c.Entry.Name, c.Entry.Dir, c.Entry.Type, uintptr(flags), "") + case Unmount: + return sysUnmount(c.Entry.Dir, unmountNoFollow) + } + return fmt.Errorf("cannot process mount change, unknown action: %q", c.Action) +} + +// NeededChanges computes the changes required to change current to desired mount entries. +// +// The current and desired profiles is a fstab like list of mount entries. The +// lists are processed and a "diff" of mount changes is produced. The mount +// changes, when applied in order, transform the current profile into the +// desired profile. +func NeededChanges(currentProfile, desiredProfile *mount.Profile) []*Change { + // Copy both profiles as we will want to mutate them. + current := make([]mount.Entry, len(currentProfile.Entries)) + copy(current, currentProfile.Entries) + desired := make([]mount.Entry, len(desiredProfile.Entries)) + copy(desired, desiredProfile.Entries) + + // Clean the directory part of both profiles. This is done so that we can + // easily test if a given directory is a subdirectory with + // strings.HasPrefix coupled with an extra slash character. + for i := range current { + current[i].Dir = path.Clean(current[i].Dir) + } + for i := range desired { + desired[i].Dir = path.Clean(desired[i].Dir) + } + + // Sort both lists by directory name with implicit trailing slash. + sort.Sort(byMagicDir(current)) + sort.Sort(byMagicDir(desired)) + + // Construct a desired directory map. + desiredMap := make(map[string]*mount.Entry) + for i := range desired { + desiredMap[desired[i].Dir] = &desired[i] + } + + // Compute reusable entries: those which are equal in current and desired and which + // are not prefixed by another entry that changed. + var reuse map[string]bool + var skipDir string + for i := range current { + dir := current[i].Dir + if skipDir != "" && strings.HasPrefix(dir, skipDir) { + continue + } + skipDir = "" // reset skip prefix as it no longer applies + if entry, ok := desiredMap[dir]; ok && current[i].Equal(entry) { + if reuse == nil { + reuse = make(map[string]bool) + } + reuse[dir] = true + continue + } + skipDir = strings.TrimSuffix(dir, "/") + "/" + } + + // We are now ready to compute the necessary mount changes. + var changes []*Change + + // Unmount entries not reused in reverse to handle children before their parent. + for i := len(current) - 1; i >= 0; i-- { + if reuse[current[i].Dir] { + changes = append(changes, &Change{Action: Keep, Entry: current[i]}) + } else { + changes = append(changes, &Change{Action: Unmount, Entry: current[i]}) + } + } + + // Mount desired entries not reused. + for i := range desired { + if !reuse[desired[i].Dir] { + changes = append(changes, &Change{Action: Mount, Entry: desired[i]}) + } + } + + return changes +} diff -Nru snapd-2.28.5/cmd/snap-update-ns/change_test.go snapd-2.29.3/cmd/snap-update-ns/change_test.go --- snapd-2.28.5/cmd/snap-update-ns/change_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-update-ns/change_test.go 2017-11-03 16:15:11.000000000 +0000 @@ -0,0 +1,239 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main_test + +import ( + "errors" + + . "gopkg.in/check.v1" + + update "github.com/snapcore/snapd/cmd/snap-update-ns" + "github.com/snapcore/snapd/interfaces/mount" + "github.com/snapcore/snapd/testutil" +) + +type changeSuite struct { + testutil.BaseTest + sys *update.SyscallRecorder +} + +var ( + errTesting = errors.New("testing") +) + +var _ = Suite(&changeSuite{}) + +func (s *changeSuite) SetUpTest(c *C) { + s.BaseTest.SetUpTest(c) + // Mock and record system interactions. + s.sys = &update.SyscallRecorder{} + s.BaseTest.AddCleanup(update.MockSystemCalls(s.sys)) +} + +func (s *changeSuite) TestString(c *C) { + change := update.Change{ + Entry: mount.Entry{Dir: "/a/b", Name: "/dev/sda1"}, + Action: update.Mount, + } + c.Assert(change.String(), Equals, "mount (/dev/sda1 /a/b none defaults 0 0)") +} + +// When there are no profiles we don't do anything. +func (s *changeSuite) TestNeededChangesNoProfiles(c *C) { + current := &mount.Profile{} + desired := &mount.Profile{} + changes := update.NeededChanges(current, desired) + c.Assert(changes, IsNil) +} + +// When the profiles are the same we don't do anything. +func (s *changeSuite) TestNeededChangesNoChange(c *C) { + current := &mount.Profile{Entries: []mount.Entry{{Dir: "/common/stuf"}}} + desired := &mount.Profile{Entries: []mount.Entry{{Dir: "/common/stuf"}}} + changes := update.NeededChanges(current, desired) + c.Assert(changes, DeepEquals, []*update.Change{ + {Entry: mount.Entry{Dir: "/common/stuf"}, Action: update.Keep}, + }) +} + +// When the content interface is connected we should mount the new entry. +func (s *changeSuite) TestNeededChangesTrivialMount(c *C) { + current := &mount.Profile{} + desired := &mount.Profile{Entries: []mount.Entry{{Dir: "/common/stuf"}}} + changes := update.NeededChanges(current, desired) + c.Assert(changes, DeepEquals, []*update.Change{ + {Entry: desired.Entries[0], Action: update.Mount}, + }) +} + +// When the content interface is disconnected we should unmount the mounted entry. +func (s *changeSuite) TestNeededChangesTrivialUnmount(c *C) { + current := &mount.Profile{Entries: []mount.Entry{{Dir: "/common/stuf"}}} + desired := &mount.Profile{} + changes := update.NeededChanges(current, desired) + c.Assert(changes, DeepEquals, []*update.Change{ + {Entry: current.Entries[0], Action: update.Unmount}, + }) +} + +// When umounting we unmount children before parents. +func (s *changeSuite) TestNeededChangesUnmountOrder(c *C) { + current := &mount.Profile{Entries: []mount.Entry{ + {Dir: "/common/stuf/extra"}, + {Dir: "/common/stuf"}, + }} + desired := &mount.Profile{} + changes := update.NeededChanges(current, desired) + c.Assert(changes, DeepEquals, []*update.Change{ + {Entry: mount.Entry{Dir: "/common/stuf/extra"}, Action: update.Unmount}, + {Entry: mount.Entry{Dir: "/common/stuf"}, Action: update.Unmount}, + }) +} + +// When mounting we mount the parents before the children. +func (s *changeSuite) TestNeededChangesMountOrder(c *C) { + current := &mount.Profile{} + desired := &mount.Profile{Entries: []mount.Entry{ + {Dir: "/common/stuf/extra"}, + {Dir: "/common/stuf"}, + }} + changes := update.NeededChanges(current, desired) + c.Assert(changes, DeepEquals, []*update.Change{ + {Entry: mount.Entry{Dir: "/common/stuf"}, Action: update.Mount}, + {Entry: mount.Entry{Dir: "/common/stuf/extra"}, Action: update.Mount}, + }) +} + +// When parent changes we don't reuse its children +func (s *changeSuite) TestNeededChangesChangedParentSameChild(c *C) { + current := &mount.Profile{Entries: []mount.Entry{ + {Dir: "/common/stuf", Name: "/dev/sda1"}, + {Dir: "/common/stuf/extra"}, + {Dir: "/common/unrelated"}, + }} + desired := &mount.Profile{Entries: []mount.Entry{ + {Dir: "/common/stuf", Name: "/dev/sda2"}, + {Dir: "/common/stuf/extra"}, + {Dir: "/common/unrelated"}, + }} + changes := update.NeededChanges(current, desired) + c.Assert(changes, DeepEquals, []*update.Change{ + {Entry: mount.Entry{Dir: "/common/unrelated"}, Action: update.Keep}, + {Entry: mount.Entry{Dir: "/common/stuf/extra"}, Action: update.Unmount}, + {Entry: mount.Entry{Dir: "/common/stuf", Name: "/dev/sda1"}, Action: update.Unmount}, + {Entry: mount.Entry{Dir: "/common/stuf", Name: "/dev/sda2"}, Action: update.Mount}, + {Entry: mount.Entry{Dir: "/common/stuf/extra"}, Action: update.Mount}, + }) +} + +// When child changes we don't touch the unchanged parent +func (s *changeSuite) TestNeededChangesSameParentChangedChild(c *C) { + current := &mount.Profile{Entries: []mount.Entry{ + {Dir: "/common/stuf"}, + {Dir: "/common/stuf/extra", Name: "/dev/sda1"}, + {Dir: "/common/unrelated"}, + }} + desired := &mount.Profile{Entries: []mount.Entry{ + {Dir: "/common/stuf"}, + {Dir: "/common/stuf/extra", Name: "/dev/sda2"}, + {Dir: "/common/unrelated"}, + }} + changes := update.NeededChanges(current, desired) + c.Assert(changes, DeepEquals, []*update.Change{ + {Entry: mount.Entry{Dir: "/common/unrelated"}, Action: update.Keep}, + {Entry: mount.Entry{Dir: "/common/stuf/extra", Name: "/dev/sda1"}, Action: update.Unmount}, + {Entry: mount.Entry{Dir: "/common/stuf"}, Action: update.Keep}, + {Entry: mount.Entry{Dir: "/common/stuf/extra", Name: "/dev/sda2"}, Action: update.Mount}, + }) +} + +// cur = ['/a/b', '/a/b-1', '/a/b-1/3', '/a/b/c'] +// des = ['/a/b', '/a/b-1', '/a/b/c' +// +// We are smart about comparing entries as directories. Here even though "/a/b" +// is a prefix of "/a/b-1" it is correctly reused. +func (s *changeSuite) TestNeededChangesSmartEntryComparison(c *C) { + current := &mount.Profile{Entries: []mount.Entry{ + {Dir: "/a/b", Name: "/dev/sda1"}, + {Dir: "/a/b-1"}, + {Dir: "/a/b-1/3"}, + {Dir: "/a/b/c"}, + }} + desired := &mount.Profile{Entries: []mount.Entry{ + {Dir: "/a/b", Name: "/dev/sda2"}, + {Dir: "/a/b-1"}, + {Dir: "/a/b/c"}, + }} + changes := update.NeededChanges(current, desired) + c.Assert(changes, DeepEquals, []*update.Change{ + {Entry: mount.Entry{Dir: "/a/b/c"}, Action: update.Unmount}, + {Entry: mount.Entry{Dir: "/a/b", Name: "/dev/sda1"}, Action: update.Unmount}, + {Entry: mount.Entry{Dir: "/a/b-1/3"}, Action: update.Unmount}, + {Entry: mount.Entry{Dir: "/a/b-1"}, Action: update.Keep}, + + {Entry: mount.Entry{Dir: "/a/b", Name: "/dev/sda2"}, Action: update.Mount}, + {Entry: mount.Entry{Dir: "/a/b/c"}, Action: update.Mount}, + }) +} + +// Change.Perform calls the mount system call. +func (s *changeSuite) TestPerformMount(c *C) { + chg := &update.Change{Action: update.Mount, Entry: mount.Entry{Name: "source", Dir: "target", Type: "type"}} + c.Assert(chg.Perform(), IsNil) + c.Assert(s.sys.Calls(), DeepEquals, []string{`mount "source" "target" "type" 0 ""`}) +} + +// Change.Perform returns errors from mount system call +func (s *changeSuite) TestPerformMountError(c *C) { + s.sys.InsertFault(`mount "source" "target" "type" 0 ""`, errTesting) + chg := &update.Change{Action: update.Mount, Entry: mount.Entry{Name: "source", Dir: "target", Type: "type"}} + c.Assert(chg.Perform(), Equals, errTesting) + c.Assert(s.sys.Calls(), DeepEquals, []string{`mount "source" "target" "type" 0 ""`}) +} + +// Change.Perform returns errors from bad flags +func (s *changeSuite) TestPerformMountOptionError(c *C) { + chg := &update.Change{Action: update.Mount, Entry: mount.Entry{Name: "source", Dir: "target", Type: "type", Options: []string{"bogus"}}} + c.Assert(chg.Perform(), ErrorMatches, `unsupported mount option: "bogus"`) + c.Assert(s.sys.Calls(), HasLen, 0) +} + +// Change.Perform calls the unmount system call. +func (s *changeSuite) TestPerformUnmount(c *C) { + chg := &update.Change{Action: update.Unmount, Entry: mount.Entry{Name: "source", Dir: "target", Type: "type"}} + c.Assert(chg.Perform(), IsNil) + // The flag 8 is UMOUNT_NOFOLLOW + c.Assert(s.sys.Calls(), DeepEquals, []string{`unmount "target" UMOUNT_NOFOLLOW`}) +} + +// Change.Perform returns errors from unmount system call +func (s *changeSuite) TestPerformUnountError(c *C) { + s.sys.InsertFault(`unmount "target" UMOUNT_NOFOLLOW`, errTesting) + chg := &update.Change{Action: update.Unmount, Entry: mount.Entry{Name: "source", Dir: "target", Type: "type"}} + c.Assert(chg.Perform(), Equals, errTesting) + c.Assert(s.sys.Calls(), DeepEquals, []string{`unmount "target" UMOUNT_NOFOLLOW`}) +} + +// Change.Perform handles unknown actions. +func (s *changeSuite) TestPerformUnknownAction(c *C) { + chg := &update.Change{Action: update.Action(42)} + c.Assert(chg.Perform(), ErrorMatches, `cannot process mount change, unknown action: .*`) + c.Assert(s.sys.Calls(), HasLen, 0) +} diff -Nru snapd-2.28.5/cmd/snap-update-ns/export_test.go snapd-2.29.3/cmd/snap-update-ns/export_test.go --- snapd-2.28.5/cmd/snap-update-ns/export_test.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/cmd/snap-update-ns/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -19,9 +19,70 @@ package main +import ( + "fmt" +) + var ( - ReadCmdline = readCmdline - FindArgv0 = findArgv0 - FindSnapName = findSnapName - PartiallyValidateSnapName = partiallyValidateSnapName + ReadCmdline = readCmdline + FindSnapName = findSnapName + FindFirstOption = findFirstOption + ValidateSnapName = validateSnapName + ProcessArguments = processArguments ) + +// SystemCalls encapsulates various system interactions performed by this module. +type SystemCalls interface { + Mount(source string, target string, fstype string, flags uintptr, data string) (err error) + Unmount(target string, flags int) (err error) +} + +// SyscallRecorder stores which system calls were invoked. +type SyscallRecorder struct { + calls []string + errors map[string]error +} + +// InsertFault makes given subsequent call to return the specified error. +func (sys *SyscallRecorder) InsertFault(call string, err error) { + if sys.errors == nil { + sys.errors = make(map[string]error) + } + sys.errors[call] = err +} + +// Calls returns the sequence of mocked calls that have been made. +func (sys *SyscallRecorder) Calls() []string { + return sys.calls +} + +// call remembers that a given call has occurred and returns a pre-arranged error, if any +func (sys *SyscallRecorder) call(call string) error { + sys.calls = append(sys.calls, call) + return sys.errors[call] +} + +func (sys *SyscallRecorder) Mount(source string, target string, fstype string, flags uintptr, data string) (err error) { + return sys.call(fmt.Sprintf("mount %q %q %q %d %q", source, target, fstype, flags, data)) +} + +func (sys *SyscallRecorder) Unmount(target string, flags int) (err error) { + if flags == unmountNoFollow { + return sys.call(fmt.Sprintf("unmount %q %s", target, "UMOUNT_NOFOLLOW")) + } + return sys.call(fmt.Sprintf("unmount %q %d", target, flags)) +} + +// MockSystemCalls replaces real system calls with those of the argument. +func MockSystemCalls(sc SystemCalls) (restore func()) { + oldSysMount := sysMount + oldSysUnmount := sysUnmount + + sysMount = sc.Mount + sysUnmount = sc.Unmount + + return func() { + sysMount = oldSysMount + sysUnmount = oldSysUnmount + } +} diff -Nru snapd-2.28.5/cmd/snap-update-ns/main.go snapd-2.29.3/cmd/snap-update-ns/main.go --- snapd-2.28.5/cmd/snap-update-ns/main.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/cmd/snap-update-ns/main.go 2017-11-03 16:15:11.000000000 +0000 @@ -28,35 +28,40 @@ "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/interfaces/mount" "github.com/snapcore/snapd/logger" - "github.com/snapcore/snapd/snap" + "github.com/snapcore/snapd/osutil" ) var opts struct { - Positionals struct { + FromSnapConfine bool `long:"from-snap-confine"` + Positionals struct { SnapName string `positional-arg-name:"SNAP_NAME" required:"yes"` } `positional-args:"true"` } +// IMPORTANT: all the code in main() until bootstrap is finished may be run +// with elevated privileges when invoking snap-update-ns from the setuid +// snap-confine. + func main() { + logger.SimpleSetup() if err := run(); err != nil { fmt.Printf("cannot update snap namespace: %s\n", err) os.Exit(1) } + // END IMPORTANT } func parseArgs(args []string) error { parser := flags.NewParser(&opts, flags.HelpFlag|flags.PassDoubleDash|flags.PassAfterNonOption) - if _, err := parser.ParseArgs(args); err != nil { - return err - } - return snap.ValidateName(opts.Positionals.SnapName) + _, err := parser.ParseArgs(args) + return err } -func run() error { - if err := parseArgs(os.Args[1:]); err != nil { - return err - } +// IMPORTANT: all the code in run() until BootStrapError() is finished may +// be run with elevated privileges when invoking snap-update-ns from +// the setuid snap-confine. +func run() error { // There is some C code that runs before main() is started. // That code always runs and sets an error condition if it fails. // Here we just check for the error. @@ -68,6 +73,12 @@ } return err } + // END IMPORTANT + + if err := parseArgs(os.Args[1:]); err != nil { + return err + } + snapName := opts.Positionals.SnapName // Lock the mount namespace so that any concurrently attempted invocations @@ -77,8 +88,18 @@ return fmt.Errorf("cannot open lock file for mount namespace of snap %q: %s", snapName, err) } defer lock.Close() - if err := lock.Lock(); err != nil { - return fmt.Errorf("cannot lock mount namespace of snap %q: %s", snapName, err) + + if opts.FromSnapConfine { + // When --from-snap-conifne is passed then we just ensure that the + // namespace is locked. This is used by snap-confine to use + // snap-update-ns to apply mount profiles. + if err := lock.TryLock(); err != osutil.ErrAlreadyLocked { + return fmt.Errorf("mount namespace of snap %q is not locked but --from-snap-confine was used", snapName) + } + } else { + if err := lock.Lock(); err != nil { + return fmt.Errorf("cannot lock mount namespace of snap %q: %s", snapName, err) + } } // Read the desired and current mount profiles. Note that missing files @@ -98,10 +119,10 @@ // Compute the needed changes and perform each change if needed, collecting // those that we managed to perform or that were performed already. - changesNeeded := mount.NeededChanges(currentBefore, desired) - var changesMade []mount.Change + changesNeeded := NeededChanges(currentBefore, desired) + var changesMade []*Change for _, change := range changesNeeded { - if change.Action == mount.Keep { + if change.Action == Keep { changesMade = append(changesMade, change) continue } @@ -116,7 +137,7 @@ // and save it back for next runs. var currentAfter mount.Profile for _, change := range changesMade { - if change.Action == mount.Mount || change.Action == mount.Keep { + if change.Action == Mount || change.Action == Keep { currentAfter.Entries = append(currentAfter.Entries, change.Entry) } } diff -Nru snapd-2.28.5/cmd/snap-update-ns/sorting.go snapd-2.29.3/cmd/snap-update-ns/sorting.go --- snapd-2.28.5/cmd/snap-update-ns/sorting.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-update-ns/sorting.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,44 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main + +import ( + "strings" + + "github.com/snapcore/snapd/interfaces/mount" +) + +// byMagicDir allows sorting an array of entries that automagically assumes +// each entry ends with a trailing slash. +type byMagicDir []mount.Entry + +func (c byMagicDir) Len() int { return len(c) } +func (c byMagicDir) Swap(i, j int) { c[i], c[j] = c[j], c[i] } +func (c byMagicDir) Less(i, j int) bool { + iDir := c[i].Dir + jDir := c[j].Dir + if !strings.HasSuffix(iDir, "/") { + iDir = iDir + "/" + } + if !strings.HasSuffix(jDir, "/") { + jDir = jDir + "/" + } + return iDir < jDir +} diff -Nru snapd-2.28.5/cmd/snap-update-ns/sorting_test.go snapd-2.29.3/cmd/snap-update-ns/sorting_test.go --- snapd-2.28.5/cmd/snap-update-ns/sorting_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/cmd/snap-update-ns/sorting_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,50 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package main + +import ( + "sort" + + . "gopkg.in/check.v1" + + "github.com/snapcore/snapd/interfaces/mount" +) + +type sortSuite struct{} + +var _ = Suite(&sortSuite{}) + +func (s *sortSuite) TestTrailingSlashesComparison(c *C) { + // Naively sorted entries. + entries := []mount.Entry{ + {Dir: "/a/b"}, + {Dir: "/a/b-1"}, + {Dir: "/a/b-1/3"}, + {Dir: "/a/b/c"}, + } + sort.Sort(byMagicDir(entries)) + // Entries sorted as if they had a trailing slash. + c.Assert(entries, DeepEquals, []mount.Entry{ + {Dir: "/a/b-1"}, + {Dir: "/a/b-1/3"}, + {Dir: "/a/b"}, + {Dir: "/a/b/c"}, + }) +} diff -Nru snapd-2.28.5/corecfg/corecfg.go snapd-2.29.3/corecfg/corecfg.go --- snapd-2.28.5/corecfg/corecfg.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/corecfg/corecfg.go 2017-10-23 06:17:27.000000000 +0000 @@ -38,8 +38,7 @@ // ensureSupportInterface checks that the system has the core-support // interface. An error is returned if this is not the case func ensureSupportInterface() error { - _, err := systemd.SystemctlCmd("--version") - return err + return systemd.Available() } func snapctlGet(key string) (string, error) { diff -Nru snapd-2.28.5/corecfg/corecfg_test.go snapd-2.29.3/corecfg/corecfg_test.go --- snapd-2.28.5/corecfg/corecfg_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/corecfg/corecfg_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -34,17 +34,22 @@ // coreCfgSuite is the base for all the corecfg tests type coreCfgSuite struct { - systemctlArgs [][]string + systemctlArgs [][]string + systemctlRestorer func() } var _ = Suite(&coreCfgSuite{}) func (s *coreCfgSuite) SetUpSuite(c *C) { - systemd.SystemctlCmd = func(args ...string) ([]byte, error) { + s.systemctlRestorer = systemd.MockSystemctl(func(args ...string) ([]byte, error) { s.systemctlArgs = append(s.systemctlArgs, args[:]) output := []byte("ActiveState=inactive") return output, nil - } + }) +} + +func (s *coreCfgSuite) TearDownSuite(c *C) { + s.systemctlRestorer() } // runCfgSuite tests corecfg.Run() @@ -66,13 +71,10 @@ restore := release.MockOnClassic(false) defer restore() - oldSystemdSystemctlCmd := systemd.SystemctlCmd - systemd.SystemctlCmd = func(args ...string) ([]byte, error) { + r := systemd.MockSystemctl(func(args ...string) ([]byte, error) { return nil, fmt.Errorf("simulate missing core-support") - } - defer func() { - systemd.SystemctlCmd = oldSystemdSystemctlCmd - }() + }) + defer r() err := corecfg.Run() c.Check(err, ErrorMatches, `(?m)cannot run systemctl - core-support interface seems disconnected: simulate missing core-support`) diff -Nru snapd-2.28.5/corecfg/picfg_test.go snapd-2.29.3/corecfg/picfg_test.go --- snapd-2.28.5/corecfg/picfg_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/corecfg/picfg_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -59,7 +59,6 @@ err := os.MkdirAll(filepath.Dir(s.mockConfigPath), 0755) c.Assert(err, IsNil) s.mockConfig(c, mockConfigTxt) - } func (s *piCfgSuite) TearDownTest(c *C) { diff -Nru snapd-2.28.5/corecfg/services_test.go snapd-2.29.3/corecfg/services_test.go --- snapd-2.28.5/corecfg/services_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/corecfg/services_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -29,23 +29,28 @@ "github.com/snapcore/snapd/corecfg" "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/release" + "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/testutil" ) type servicesSuite struct { coreCfgSuite + testutil.BaseTest } var _ = Suite(&servicesSuite{}) func (s *servicesSuite) SetUpTest(c *C) { + s.BaseTest.SetUpTest(c) dirs.SetRootDir(c.MkDir()) c.Assert(os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "etc"), 0755), IsNil) s.systemctlArgs = nil + s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) } func (s *servicesSuite) TearDownTest(c *C) { dirs.SetRootDir("/") + s.BaseTest.TearDownTest(c) } func (s *servicesSuite) TestConfigureServiceInvalidValue(c *C) { diff -Nru snapd-2.28.5/daemon/api.go snapd-2.29.3/daemon/api.go --- snapd-2.28.5/daemon/api.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/daemon/api.go 2017-11-09 18:15:21.000000000 +0000 @@ -44,21 +44,22 @@ "github.com/snapcore/snapd/asserts/snapasserts" "github.com/snapcore/snapd/client" "github.com/snapcore/snapd/dirs" - "github.com/snapcore/snapd/i18n/dumb" + "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/jsonutil" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/overlord/assertstate" "github.com/snapcore/snapd/overlord/auth" - "github.com/snapcore/snapd/overlord/cmdstate" "github.com/snapcore/snapd/overlord/configstate" "github.com/snapcore/snapd/overlord/configstate/config" "github.com/snapcore/snapd/overlord/devicestate" "github.com/snapcore/snapd/overlord/hookstate/ctlcmd" "github.com/snapcore/snapd/overlord/ifacestate" + "github.com/snapcore/snapd/overlord/servicestate" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" "github.com/snapcore/snapd/progress" "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" @@ -155,9 +156,9 @@ } logsCmd = &Command{ - Path: "/v2/logs", - UserOK: true, - GET: getLogs, + Path: "/v2/logs", + PolkitOK: "io.snapcraft.snapd.manage", + GET: getLogs, } snapConfCmd = &Command{ @@ -188,10 +189,11 @@ } stateChangeCmd = &Command{ - Path: "/v2/changes/{id}", - UserOK: true, - GET: getChange, - POST: abortChange, + Path: "/v2/changes/{id}", + UserOK: true, + PolkitOK: "io.snapcraft.snapd.manage", + GET: getChange, + POST: abortChange, } stateChangesCmd = &Command{ @@ -505,33 +507,29 @@ return SyncResponse(result, nil) } -func webify(result map[string]interface{}, resource string) map[string]interface{} { - result["resource"] = resource - - icon, ok := result["icon"].(string) - if !ok || icon == "" || strings.HasPrefix(icon, "http") { +func webify(result *client.Snap, resource string) *client.Snap { + if result.Icon == "" || strings.HasPrefix(result.Icon, "http") { return result } - result["icon"] = "" + result.Icon = "" route := appIconCmd.d.router.Get(appIconCmd.Path) if route != nil { - name, _ := result["name"].(string) - url, err := route.URL("name", name) + url, err := route.URL("name", result.Name) if err == nil { - result["icon"] = url.String() + result.Icon = url.String() } } return result } -func getStore(c *Command) snapstate.StoreService { +func getStore(c *Command) storestate.StoreService { st := c.d.overlord.State() st.Lock() defer st.Unlock() - return snapstate.Store(st) + return storestate.Store(st) } func getSections(c *Command, r *http.Request, user *auth.UserState) Response { @@ -548,7 +546,7 @@ // pass case store.ErrEmptyQuery, store.ErrBadQuery: return BadRequest("%v", err) - case store.ErrUnauthenticated: + case store.ErrUnauthenticated, store.ErrInvalidCredentials: return Unauthorized("%v", err) default: return InternalError("%v", err) @@ -609,7 +607,7 @@ // pass case store.ErrEmptyQuery, store.ErrBadQuery: return BadRequest("%v", err) - case store.ErrUnauthenticated: + case store.ErrUnauthenticated, store.ErrInvalidCredentials: return Unauthorized(err.Error()) default: return InternalError("%v", err) @@ -634,10 +632,14 @@ AnyChannel: true, } snapInfo, err := theStore.SnapInfo(spec, user) - if err != nil { - if err == store.ErrSnapNotFound { - return SnapNotFound(name, err) - } + switch err { + case nil: + // pass + case store.ErrInvalidCredentials: + return Unauthorized("%v", err) + case store.ErrSnapNotFound: + return SnapNotFound(name, err) + default: return InternalError("%v", err) } @@ -799,7 +801,7 @@ } type snapInstruction struct { - progress.NullProgress + progress.NullMeter Action string `json:"action"` Channel string `json:"channel"` Revision snap.Revision `json:"revision"` @@ -834,7 +836,6 @@ } var ( - snapstateCoreInfo = snapstate.CoreInfo snapstateInstall = snapstate.Install snapstateInstallPath = snapstate.InstallPath snapstateRefreshCandidates = snapstate.RefreshCandidates @@ -858,42 +859,6 @@ var errNothingToInstall = errors.New("nothing to install") -const oldDefaultSnapCoreName = "ubuntu-core" -const defaultCoreSnapName = "core" - -func ensureCore(st *state.State, targetSnap string, userID int) (*state.TaskSet, error) { - if targetSnap == defaultCoreSnapName || targetSnap == oldDefaultSnapCoreName { - return nil, errNothingToInstall - } - - _, err := snapstateCoreInfo(st) - if err != state.ErrNoState { - return nil, err - } - - return snapstateInstall(st, defaultCoreSnapName, "stable", snap.R(0), userID, snapstate.Flags{}) -} - -func withEnsureCore(st *state.State, targetSnap string, userID int, install func() (*state.TaskSet, error)) ([]*state.TaskSet, error) { - ubuCoreTs, err := ensureCore(st, targetSnap, userID) - if err != nil && err != errNothingToInstall { - return nil, err - } - - ts, err := install() - if err != nil { - return nil, err - } - - // ensure main install waits on ubuntu core install - if ubuCoreTs != nil { - ts.WaitAll(ubuCoreTs) - return []*state.TaskSet{ubuCoreTs, ts}, nil - } - - return []*state.TaskSet{ts}, nil -} - var errDevJailModeConflict = errors.New("cannot use devmode and jailmode flags together") var errClassicDevmodeConflict = errors.New("cannot use classic and devmode flags together") var errNoJailMode = errors.New("this system cannot honour the jailmode flag") @@ -991,11 +956,7 @@ logger.Noticef("Installing snap %q revision %s", inst.Snaps[0], inst.Revision) - tsets, err := withEnsureCore(st, inst.Snaps[0], inst.userID, - func() (*state.TaskSet, error) { - return snapstateInstall(st, inst.Snaps[0], inst.Channel, inst.Revision, inst.userID, flags) - }, - ) + tset, err := snapstateInstall(st, inst.Snaps[0], inst.Channel, inst.Revision, inst.userID, flags) if err != nil { return "", nil, err } @@ -1004,7 +965,7 @@ if inst.Channel != "stable" && inst.Channel != "" { msg = fmt.Sprintf(i18n.G("Install %q snap from %q channel"), inst.Snaps[0], inst.Channel) } - return msg, tsets, nil + return msg, []*state.TaskSet{tset}, nil } func snapUpdate(inst *snapInstruction, st *state.State) (string, []*state.TaskSet, error) { @@ -1263,21 +1224,13 @@ return BadRequest("cannot read snap info for %s: %s", trydir, err) } - var userID int - if user != nil { - userID = user.ID - } - tsets, err := withEnsureCore(st, info.Name(), userID, - func() (*state.TaskSet, error) { - return snapstateTryPath(st, info.Name(), trydir, flags) - }, - ) + tset, err := snapstateTryPath(st, info.Name(), trydir, flags) if err != nil { return BadRequest("cannot try %s: %s", trydir, err) } msg := fmt.Sprintf(i18n.G("Try %q snap from %s"), info.Name(), trydir) - chg := newChange(st, "try-snap", msg, tsets, []string{info.Name()}) + chg := newChange(st, "try-snap", msg, []*state.TaskSet{tset}, []string{info.Name()}) chg.Set("api-data", map[string]string{"snap-name": info.Name()}) ensureStateSoon(st) @@ -1453,11 +1406,11 @@ if !dangerousOK { si, err := snapasserts.DeriveSideInfo(tempPath, assertstate.DB(st)) - switch err { - case nil: + switch { + case err == nil: snapName = si.RealName sideInfo = si - case asserts.ErrNotFound: + case asserts.IsNotFound(err): // with devmode we try to find assertions but it's ok // if they are not there (implies --dangerous) if !isTrue(form, "devmode") { @@ -1488,21 +1441,12 @@ msg = fmt.Sprintf(i18n.G("Install %q snap from file %q"), snapName, origPath) } - var userID int - if user != nil { - userID = user.ID - } - - tsets, err := withEnsureCore(st, snapName, userID, - func() (*state.TaskSet, error) { - return snapstateInstallPath(st, sideInfo, tempPath, "", flags) - }, - ) + tset, err := snapstateInstallPath(st, sideInfo, tempPath, "", flags) if err != nil { return InternalError("cannot install snap file: %v", err) } - chg := newChange(st, "install-snap", msg, tsets, []string{snapName}) + chg := newChange(st, "install-snap", msg, []*state.TaskSet{tset}, []string{snapName}) chg.Set("api-data", map[string]string{"snap-name": snapName}) ensureStateSoon(st) @@ -1568,9 +1512,6 @@ snapName := vars["name"] keys := splitQS(r.URL.Query().Get("keys")) - if len(keys) == 0 { - return BadRequest("cannot obtain configuration: no keys supplied") - } s := c.d.overlord.State() s.Lock() @@ -1578,15 +1519,30 @@ s.Unlock() currentConfValues := make(map[string]interface{}) + // Special case - return root document + if len(keys) == 0 { + keys = []string{""} + } for _, key := range keys { var value interface{} if err := tr.Get(snapName, key, &value); err != nil { if config.IsNoOption(err) { + if key == "" { + // no configuration - return empty document + currentConfValues = make(map[string]interface{}) + break + } return BadRequest("%v", err) } else { return InternalError("%v", err) } } + if key == "" { + if len(keys) > 1 { + return BadRequest("keys contains zero-length string") + } + return SyncResponse(value, nil) + } currentConfValues[key] = value } @@ -1830,7 +1786,7 @@ state.Unlock() assertions, err := db.FindMany(assertType, headers) - if err == asserts.ErrNotFound { + if asserts.IsNotFound(err) { return AssertResponse(nil, true) } else if err != nil { return InternalError("searching assertions failed: %v", err) @@ -2068,7 +2024,7 @@ st.Lock() assertions, err := db.FindMany(asserts.SystemUserType, headers) st.Unlock() - if err != nil && err != asserts.ErrNotFound { + if err != nil && !asserts.IsNotFound(err) { return BadRequest("cannot find system-user assertion: %s", err) } @@ -2636,13 +2592,16 @@ if rsp != nil { return rsp } + if len(appInfos) == 0 { + return AppNotFound("no matching services") + } serviceNames := make([]string, len(appInfos)) for i, appInfo := range appInfos { serviceNames[i] = appInfo.ServiceName() } - sysd := systemd.New(dirs.GlobalRootDir, &progress.NullProgress{}) + sysd := systemd.New(dirs.GlobalRootDir, progress.Null) reader, err := sysd.LogReader(serviceNames, n, follow) if err != nil { return InternalError("cannot get logs: %v", err) @@ -2654,16 +2613,8 @@ } } -type appInstruction struct { - Action string `json:"action"` - Names []string `json:"names"` - client.StartOptions - client.StopOptions - client.RestartOptions -} - func postApps(c *Command, r *http.Request, user *auth.UserState) Response { - var inst appInstruction + var inst servicestate.Instruction decoder := json.NewDecoder(r.Body) if err := decoder.Decode(&inst); err != nil { return BadRequest("cannot decode request body into service operation: %v", err) @@ -2684,56 +2635,16 @@ return InternalError("no services found") } - // the argv to call systemctl will need at most one entry per appInfo, - // plus one for "systemctl", one for the action, and sometimes one for - // an option. That's a maximum of 3+len(appInfos). - argv := make([]string, 2, 3+len(appInfos)) - argv[0] = "systemctl" - - argv[1] = inst.Action - switch inst.Action { - case "start": - if inst.Enable { - argv[1] = "enable" - argv = append(argv, "--now") - } - case "stop": - if inst.Disable { - argv[1] = "disable" - argv = append(argv, "--now") - } - case "restart": - if inst.Reload { - argv[1] = "reload-or-restart" - } - default: - return BadRequest("unknown action %q", inst.Action) - } - - snapNames := make([]string, 0, len(appInfos)) - lastName := "" - names := make([]string, len(appInfos)) - for i, svc := range appInfos { - argv = append(argv, svc.ServiceName()) - snapName := svc.Snap.Name() - names[i] = snapName + "." + svc.Name - if snapName != lastName { - snapNames = append(snapNames, snapName) - lastName = snapName + ts, err := servicestate.Control(st, appInfos, &inst) + if err != nil { + if _, ok := err.(servicestate.ServiceActionConflictError); ok { + return Conflict(err.Error()) } + return BadRequest(err.Error()) } - - desc := fmt.Sprintf("%s of %v", inst.Action, names) - st.Lock() defer st.Unlock() - if err := snapstate.CheckChangeConflictMany(st, snapNames, nil); err != nil { - return InternalError(err.Error()) - } - - ts := cmdstate.Exec(st, desc, argv) - chg := st.NewChange("service-control", desc) - chg.AddAll(ts) + chg := newChange(st, "service-control", fmt.Sprintf("Running service command"), []*state.TaskSet{ts}, inst.Names) st.EnsureBefore(0) return AsyncResponse(nil, &Meta{Change: chg.ID()}) } diff -Nru snapd-2.28.5/daemon/api_test.go snapd-2.29.3/daemon/api_test.go --- snapd-2.28.5/daemon/api_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/daemon/api_test.go 2017-11-09 18:14:36.000000000 +0000 @@ -46,6 +46,7 @@ "golang.org/x/crypto/sha3" "gopkg.in/check.v1" "gopkg.in/macaroon.v1" + "gopkg.in/tomb.v2" "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/asserts/assertstest" @@ -55,12 +56,15 @@ "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/ifacetest" "github.com/snapcore/snapd/osutil" + "github.com/snapcore/snapd/overlord" "github.com/snapcore/snapd/overlord/assertstate" "github.com/snapcore/snapd/overlord/auth" "github.com/snapcore/snapd/overlord/configstate/config" "github.com/snapcore/snapd/overlord/ifacestate" + "github.com/snapcore/snapd/overlord/servicestate" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/snap/snaptest" @@ -88,17 +92,17 @@ restoreRelease func() trustedRestorer func() - origSysctlCmd func(...string) ([]byte, error) - sysctlArgses [][]string - sysctlBufs [][]byte - sysctlErrs []error - - origJctlCmd func([]string, string, bool) (io.ReadCloser, error) - jctlSvcses [][]string - jctlNs []string - jctlFollows []bool - jctlRCs []io.ReadCloser - jctlErrs []error + systemctlRestorer func() + sysctlArgses [][]string + sysctlBufs [][]byte + sysctlErrs []error + + journalctlRestorer func() + jctlSvcses [][]string + jctlNs []string + jctlFollows []bool + jctlRCs []io.ReadCloser + jctlErrs []error } func (s *apiBaseSuite) SnapInfo(spec store.SnapSpec, user *auth.UserState) (*snap.Info, error) { @@ -155,18 +159,15 @@ func (s *apiBaseSuite) SetUpSuite(c *check.C) { muxVars = s.muxVars s.restoreRelease = release.MockForcedDevmode(false) - - snapstate.CanAutoRefresh = nil - s.origSysctlCmd = systemd.SystemctlCmd - s.origJctlCmd = systemd.JournalctlCmd - systemd.SystemctlCmd = s.systemctl - systemd.JournalctlCmd = s.journalctl + s.systemctlRestorer = systemd.MockSystemctl(s.systemctl) + s.journalctlRestorer = systemd.MockJournalctl(s.journalctl) } func (s *apiBaseSuite) TearDownSuite(c *check.C) { muxVars = nil s.restoreRelease() - systemd.SystemctlCmd = s.origSysctlCmd + s.systemctlRestorer() + s.journalctlRestorer() } func (s *apiBaseSuite) systemctl(args ...string) (buf []byte, err error) { @@ -234,7 +235,6 @@ s.trustedRestorer = sysdb.InjectTrusted(s.storeSigning.Trusted) assertstateRefreshSnapDeclarations = nil - snapstateCoreInfo = nil snapstateInstall = nil snapstateInstallMany = nil snapstateInstallPath = nil @@ -256,7 +256,6 @@ dirs.SetRootDir("") assertstateRefreshSnapDeclarations = assertstate.RefreshSnapDeclarations - snapstateCoreInfo = snapstate.CoreInfo snapstateInstall = snapstate.Install snapstateInstallMany = snapstate.InstallMany snapstateInstallPath = snapstate.InstallPath @@ -280,7 +279,7 @@ st := d.overlord.State() st.Lock() defer st.Unlock() - snapstate.ReplaceStore(st, s) + storestate.ReplaceStore(st, s) // mark as already seeded st.Set("seeded", true) // registered @@ -290,10 +289,83 @@ Serial: "serialserial", }) + // don't actually try to talk to the store on snapstate.Ensure + // needs doing after the call to devicestate.Manager (which + // happens in daemon.New via overlord.New) + snapstate.CanAutoRefresh = nil + + s.d = d + return d +} + +func (s *apiBaseSuite) daemonWithOverlordMock(c *check.C) *Daemon { + if s.d != nil { + panic("called daemon() twice") + } + d, err := New() + c.Assert(err, check.IsNil) + d.addRoutes() + + o := overlord.Mock() + d.overlord = o + + st := d.overlord.State() + // adds an assertion db + assertstate.Manager(st) + st.Lock() + defer st.Unlock() + storestate.ReplaceStore(st, s) + s.d = d return d } +type fakeSnapManager struct { + runner *state.TaskRunner +} + +func newFakeSnapManager(st *state.State) *fakeSnapManager { + runner := state.NewTaskRunner(st) + + runner.AddHandler("fake-install-snap", func(t *state.Task, _ *tomb.Tomb) error { + return nil + }, nil) + runner.AddHandler("fake-install-snap-error", func(t *state.Task, _ *tomb.Tomb) error { + return fmt.Errorf("fake-install-snap-error errored") + }, nil) + + return &fakeSnapManager{runner: runner} +} + +func (m *fakeSnapManager) Ensure() error { + m.runner.Ensure() + return nil +} + +func (m *fakeSnapManager) Wait() { + m.runner.Wait() +} + +func (m *fakeSnapManager) Stop() { + m.runner.Stop() +} + +// sanity +var _ overlord.StateManager = (*fakeSnapManager)(nil) + +func (s *apiBaseSuite) daemonWithFakeSnapManager(c *check.C) *Daemon { + d := s.daemonWithOverlordMock(c) + st := d.overlord.State() + d.overlord.AddManager(newFakeSnapManager(st)) + return d +} + +func (s *apiBaseSuite) waitTrivialChange(c *check.C, chg *state.Change) { + err := s.d.overlord.Settle(5 * time.Second) + c.Assert(err, check.IsNil) + c.Assert(chg.IsReady(), check.Equals, true) +} + func (s *apiBaseSuite) mkInstalled(c *check.C, name, developer, version string, revision snap.Revision, active bool, extraYaml string) *snap.Info { return s.mkInstalledInState(c, nil, name, developer, version, revision, active, extraYaml) } @@ -468,41 +540,40 @@ c.Assert(ok, check.Equals, true) c.Assert(rsp, check.NotNil) - c.Assert(rsp.Result, check.FitsTypeOf, map[string]interface{}{}) - m := rsp.Result.(map[string]interface{}) + c.Assert(rsp.Result, check.FitsTypeOf, &client.Snap{}) + m := rsp.Result.(*client.Snap) // installed-size depends on vagaries of the filesystem, just check type - c.Check(m["installed-size"], check.FitsTypeOf, int64(0)) - delete(m, "installed-size") + c.Check(m.InstalledSize, check.FitsTypeOf, int64(0)) + m.InstalledSize = 0 // ditto install-date - c.Check(m["install-date"], check.FitsTypeOf, time.Time{}) - delete(m, "install-date") + c.Check(m.InstallDate, check.FitsTypeOf, time.Time{}) + m.InstallDate = time.Time{} meta := &Meta{} expected := &resp{ Type: ResponseTypeSync, Status: 200, - Result: map[string]interface{}{ - "id": "foo-id", - "name": "foo", - "revision": snap.R(10), - "version": "v1", - "channel": "stable", - "tracking-channel": "beta", - "title": "title", - "summary": "summary", - "description": "description", - "developer": "bar", - "status": "active", - "icon": "/v2/icons/foo/icon", - "type": string(snap.TypeApp), - "resource": "/v2/snaps/foo", - "private": false, - "devmode": false, - "jailmode": false, - "confinement": snap.StrictConfinement, - "trymode": false, - "apps": []*client.AppInfo{ + Result: &client.Snap{ + ID: "foo-id", + Name: "foo", + Revision: snap.R(10), + Version: "v1", + Channel: "stable", + TrackingChannel: "beta", + Title: "title", + Summary: "summary", + Description: "description", + Developer: "bar", + Status: "active", + Icon: "/v2/icons/foo/icon", + Type: string(snap.TypeApp), + Private: false, + DevMode: false, + JailMode: false, + Confinement: string(snap.StrictConfinement), + TryMode: false, + Apps: []client.AppInfo{ { Snap: "foo", Name: "cmd", DesktopFile: df, @@ -533,9 +604,9 @@ Active: false, }, }, - "broken": "", - "contact": "", - "license": "GPL-3.0", + Broken: "", + Contact: "", + License: "GPL-3.0", }, Meta: meta, } @@ -623,8 +694,6 @@ "maxReadBuflen", "muxVars", "errNothingToInstall", - "defaultCoreSnapName", - "oldDefaultSnapCoreName", "errDevJailModeConflict", "errNoJailMode", "errClassicDevmodeConflict", @@ -634,7 +703,6 @@ "snapstateUpdate", "snapstateInstallPath", "snapstateTryPath", - "snapstateCoreInfo", "snapstateUpdateMany", "snapstateInstallMany", "snapstateRemoveMany", @@ -1098,7 +1166,7 @@ c.Check(rsp.Type, check.Equals, ResponseTypeError) c.Check(rsp.Status, check.Equals, 401) - c.Check(rsp.Result.(*errorResult).Message, testutil.Contains, "cannot authenticate to snap store") + c.Check(rsp.Result.(*errorResult).Message, check.Equals, "invalid credentials") } func (s *apiSuite) TestUserFromRequestNoHeader(c *check.C) { @@ -1336,7 +1404,7 @@ c.Assert(snaps, check.HasLen, 1) c.Assert(snaps[0]["name"], check.Equals, "store") c.Check(snaps[0]["prices"], check.IsNil) - c.Check(snaps[0]["screenshots"], check.IsNil) + c.Check(snaps[0]["screenshots"], check.HasLen, 0) c.Check(snaps[0]["channels"], check.IsNil) c.Check(rsp.SuggestedCurrency, check.Equals, "EUR") @@ -1792,9 +1860,7 @@ } func (s *apiSuite) TestPostSnap(c *check.C) { - d := s.daemon(c) - d.overlord.Loop() - defer d.overlord.Stop() + d := s.daemonWithOverlordMock(c) soon := 0 ensureStateSoon = func(st *state.State) { @@ -1834,9 +1900,7 @@ } func (s *apiSuite) TestPostSnapVerfySnapInstruction(c *check.C) { - d := s.daemon(c) - d.overlord.Loop() - defer d.overlord.Stop() + s.daemonWithOverlordMock(c) buf := bytes.NewBufferString(`{"action": "install"}`) req, err := http.NewRequest("POST", "/v2/snaps/ubuntu-core", buf) @@ -1943,7 +2007,7 @@ // try a multipart/form-data upload body := sideLoadBodyWithoutDevMode head := map[string]string{"Content-Type": "multipart/thing; boundary=--hello--"} - chgSummary := s.sideloadCheck(c, body, head, snapstate.Flags{RemoveSnapPath: true}, false) + chgSummary := s.sideloadCheck(c, body, head, snapstate.Flags{RemoveSnapPath: true}) c.Check(chgSummary, check.Equals, `Install "local" snap from file "a/b/local.snap"`) } @@ -1954,7 +2018,7 @@ restore := release.MockForcedDevmode(true) defer restore() flags := snapstate.Flags{RemoveSnapPath: true} - chgSummary := s.sideloadCheck(c, body, head, flags, false) + chgSummary := s.sideloadCheck(c, body, head, flags) c.Check(chgSummary, check.Equals, `Install "local" snap from file "a/b/local.snap"`) } @@ -1973,7 +2037,7 @@ // try a multipart/form-data upload flags := snapstate.Flags{RemoveSnapPath: true} flags.DevMode = true - chgSummary := s.sideloadCheck(c, body, head, flags, true) + chgSummary := s.sideloadCheck(c, body, head, flags) c.Check(chgSummary, check.Equals, `Install "local" snap from file "x"`) } @@ -1995,7 +2059,7 @@ head := map[string]string{"Content-Type": "multipart/thing; boundary=--hello--"} // try a multipart/form-data upload flags := snapstate.Flags{JailMode: true, RemoveSnapPath: true} - chgSummary := s.sideloadCheck(c, body, head, flags, true) + chgSummary := s.sideloadCheck(c, body, head, flags) c.Check(chgSummary, check.Equals, `Install "local" snap from file "x"`) } @@ -2014,9 +2078,7 @@ "\r\n" + "true\r\n" + "----hello--\r\n" - d := s.daemon(c) - d.overlord.Loop() - defer d.overlord.Stop() + s.daemonWithOverlordMock(c) req, err := http.NewRequest("POST", "/v2/snaps", bytes.NewBufferString(body)) c.Assert(err, check.IsNil) @@ -2038,9 +2100,7 @@ "\r\n" + "true\r\n" + "----hello--\r\n" - d := s.daemon(c) - d.overlord.Loop() - defer d.overlord.Stop() + s.daemonWithOverlordMock(c) req, err := http.NewRequest("POST", "/v2/snaps", bytes.NewBufferString(body)) c.Assert(err, check.IsNil) @@ -2055,9 +2115,7 @@ } func (s *apiSuite) TestLocalInstallSnapDeriveSideInfo(c *check.C) { - d := s.daemon(c) - d.overlord.Loop() - defer d.overlord.Stop() + d := s.daemonWithOverlordMock(c) // add the assertions first st := d.overlord.State() assertAdd(st, s.storeSigning.StoreAccountKey("")) @@ -2096,9 +2154,6 @@ c.Assert(err, check.IsNil) req.Header.Set("Content-Type", "multipart/thing; boundary=--hello--") - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - return nil, nil - } snapstateInstallPath = func(s *state.State, si *snap.SideInfo, path, channel string, flags snapstate.Flags) (*state.TaskSet, error) { c.Check(flags, check.Equals, snapstate.Flags{RemoveSnapPath: true}) c.Check(si, check.DeepEquals, &snap.SideInfo{ @@ -2137,9 +2192,7 @@ "\r\n" + "xyzzy\r\n" + "----hello--\r\n" - d := s.daemon(c) - d.overlord.Loop() - defer d.overlord.Stop() + s.daemonWithOverlordMock(c) req, err := http.NewRequest("POST", "/v2/snaps", bytes.NewBufferString(body)) c.Assert(err, check.IsNil) @@ -2180,9 +2233,7 @@ } func (s *apiSuite) TestTrySnap(c *check.C) { - d := s.daemon(c) - d.overlord.Loop() - defer d.overlord.Stop() + d := s.daemonWithFakeSnapManager(c) var err error @@ -2235,22 +2286,13 @@ defer st.Unlock() for _, t := range []struct { - coreInfoErr error - nTasks int - installSnap string - flags snapstate.Flags - desc string + flags snapstate.Flags + desc string }{ - // core installed - {nil, 1, "", snapstate.Flags{}, "core; -"}, - {nil, 1, "", snapstate.Flags{DevMode: true}, "core; devmode"}, - {nil, 1, "", snapstate.Flags{JailMode: true}, "core; jailmode"}, - {nil, 1, "", snapstate.Flags{Classic: true}, "core; classic"}, - // no-core-installed - {state.ErrNoState, 2, "core", snapstate.Flags{}, "no core; -"}, - {state.ErrNoState, 2, "core", snapstate.Flags{DevMode: true}, "no core; devmode"}, - {state.ErrNoState, 2, "core", snapstate.Flags{JailMode: true}, "no core; jailmode"}, - {state.ErrNoState, 2, "core", snapstate.Flags{Classic: true}, "no core; classic"}, + {snapstate.Flags{}, "core; -"}, + {snapstate.Flags{DevMode: true}, "core; devmode"}, + {snapstate.Flags{JailMode: true}, "core; jailmode"}, + {snapstate.Flags{Classic: true}, "core; classic"}, } { soon := 0 ensureStateSoon = func(st *state.State) { @@ -2266,20 +2308,14 @@ return state.NewTaskSet(t), nil } - installSnap := "" snapstateInstall = func(s *state.State, name, channel string, revision snap.Revision, userID int, flags snapstate.Flags) (*state.TaskSet, error) { if name != "core" { c.Check(flags, check.DeepEquals, t.flags, check.Commentf(t.desc)) } - installSnap = name t := s.NewTask("fake-install-snap", "Doing a fake install") return state.NewTaskSet(t), nil } - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - return nil, t.coreInfoErr - } - // try the snap (without an installed core) st.Unlock() rsp := postSnaps(snapsCmd, reqForFlags(t.flags), nil).(*resp) @@ -2290,11 +2326,10 @@ chg := st.Change(rsp.Change) c.Assert(chg, check.NotNil, check.Commentf(t.desc)) - c.Assert(chg.Tasks(), check.HasLen, t.nTasks, check.Commentf(t.desc)) - c.Check(installSnap, check.Equals, t.installSnap, check.Commentf(t.desc)) + c.Assert(chg.Tasks(), check.HasLen, 1, check.Commentf(t.desc)) st.Unlock() - <-chg.Ready() + s.waitTrivialChange(c, chg) st.Lock() c.Check(chg.Kind(), check.Equals, "try-snap", check.Commentf(t.desc)) @@ -2332,10 +2367,8 @@ c.Check(rsp.Result.(*errorResult).Message, testutil.Contains, "not a snap directory") } -func (s *apiSuite) sideloadCheck(c *check.C, content string, head map[string]string, expectedFlags snapstate.Flags, hasCoreSnap bool) string { - d := s.daemon(c) - d.overlord.Loop() - defer d.overlord.Stop() +func (s *apiSuite) sideloadCheck(c *check.C, content string, head map[string]string, expectedFlags snapstate.Flags) string { + d := s.daemonWithFakeSnapManager(c) soon := 0 ensureStateSoon = func(st *state.State) { @@ -2349,13 +2382,6 @@ return &snap.Info{SuggestedName: "local"}, nil } - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - if hasCoreSnap { - return nil, nil - } - // pretend we do not have a state for ubuntu-core - return nil, state.ErrNoState - } snapstateInstall = func(s *state.State, name, channel string, revision snap.Revision, userID int, flags snapstate.Flags) (*state.TaskSet, error) { // NOTE: ubuntu-core is not installed in developer mode c.Check(flags, check.Equals, snapstate.Flags{}) @@ -2387,13 +2413,7 @@ rsp := postSnaps(snapsCmd, req, nil).(*resp) c.Assert(rsp.Type, check.Equals, ResponseTypeAsync) n := 1 - if !hasCoreSnap { - n++ - } c.Assert(installQueue, check.HasLen, n) - if !hasCoreSnap { - c.Check(installQueue[0], check.Equals, defaultCoreSnapName) - } c.Check(installQueue[n-1], check.Matches, "local::.*/snapd-sideload-pkg-.*") st := d.overlord.State() @@ -2407,7 +2427,7 @@ c.Assert(chg.Tasks(), check.HasLen, n) st.Unlock() - <-chg.Ready() + s.waitTrivialChange(c, chg) st.Lock() c.Check(chg.Kind(), check.Equals, "install-snap") @@ -2462,9 +2482,17 @@ c.Check(result, check.DeepEquals, map[string]interface{}{"message": `snap "test-snap" has no "test-key2" configuration option`}) } -func (s *apiSuite) TestGetConfNoKey(c *check.C) { - result := s.runGetConf(c, nil, 400) - c.Check(result, check.DeepEquals, map[string]interface{}{"message": "cannot obtain configuration: no keys supplied"}) +func (s *apiSuite) TestGetRootDocument(c *check.C) { + d := s.daemon(c) + d.overlord.State().Lock() + tr := config.NewTransaction(d.overlord.State()) + tr.Set("test-snap", "test-key1", "test-value1") + tr.Set("test-snap", "test-key2", "test-value2") + tr.Commit() + d.overlord.State().Unlock() + + result := s.runGetConf(c, nil, 200) + c.Check(result, check.DeepEquals, map[string]interface{}{"test-key1": "test-value1", "test-key2": "test-value2"}) } func (s *apiSuite) TestGetConfBadKey(c *check.C) { @@ -2567,14 +2595,7 @@ } func (s *apiSuite) TestSetConfBadSnap(c *check.C) { - d := s.daemon(c) - - // Mock the hook runner - hookRunner := testutil.MockCommand(c, "snap", "") - defer hookRunner.Restore() - - d.overlord.Loop() - defer d.overlord.Stop() + s.daemonWithOverlordMock(c) text, err := json.Marshal(map[string]interface{}{"key": "value"}) c.Assert(err, check.IsNil) @@ -2702,10 +2723,6 @@ restore := release.MockForcedDevmode(forcedDevmode) defer restore() - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - // we have core - return nil, nil - } snapstateInstall = func(s *state.State, name, channel string, revno snap.Revision, userID int, flags snapstate.Flags) (*state.TaskSet, error) { calledFlags = flags installQueue = append(installQueue, name) @@ -2716,14 +2733,10 @@ } defer func() { - snapstateCoreInfo = nil snapstateInstall = nil }() - d := s.daemon(c) - - d.overlord.Loop() - defer d.overlord.Stop() + d := s.daemonWithFakeSnapManager(c) var buf bytes.Buffer if revision.Unset() { @@ -2748,7 +2761,7 @@ c.Check(chg.Tasks(), check.HasLen, 1) st.Unlock() - <-chg.Ready() + s.waitTrivialChange(c, chg) st.Lock() c.Check(chg.Status(), check.Equals, state.DoneStatus) @@ -2765,10 +2778,6 @@ installQueue := []string{} assertstateCalledUserID := 0 - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - // we have core - return nil, nil - } snapstateUpdate = func(s *state.State, name, channel string, revision snap.Revision, userID int, flags snapstate.Flags) (*state.TaskSet, error) { calledFlags = flags calledUserID = userID @@ -2808,10 +2817,6 @@ calledUserID := 0 installQueue := []string{} - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - // we have core - return nil, nil - } snapstateUpdate = func(s *state.State, name, channel string, revision snap.Revision, userID int, flags snapstate.Flags) (*state.TaskSet, error) { calledFlags = flags calledUserID = userID @@ -2850,10 +2855,6 @@ func (s *apiSuite) TestRefreshClassic(c *check.C) { var calledFlags snapstate.Flags - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - // we have ubuntu-core - return nil, nil - } snapstateUpdate = func(s *state.State, name, channel string, revision snap.Revision, userID int, flags snapstate.Flags) (*state.TaskSet, error) { calledFlags = flags return nil, nil @@ -2884,10 +2885,6 @@ calledUserID := 0 installQueue := []string{} - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - // we have ubuntu-core - return nil, nil - } snapstateUpdate = func(s *state.State, name, channel string, revision snap.Revision, userID int, flags snapstate.Flags) (*state.TaskSet, error) { calledFlags = flags calledUserID = userID @@ -2932,9 +2929,7 @@ return []string{"fake1", "fake2"}, []*state.TaskSet{state.NewTaskSet(t)}, nil } - d := s.daemon(c) - d.overlord.Loop() - defer d.overlord.Stop() + d := s.daemonWithOverlordMock(c) buf := bytes.NewBufferString(`{"action": "refresh"}`) req, err := http.NewRequest("POST", "/v2/login", buf) @@ -3099,103 +3094,13 @@ c.Check(removes, check.DeepEquals, inst.Snaps) } -func (s *apiSuite) TestInstallMissingCoreSnap(c *check.C) { - installQueue := []*state.Task{} - - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - // pretend we do not have a core - return nil, state.ErrNoState - } - snapstateInstall = func(s *state.State, name, channel string, revision snap.Revision, userID int, flags snapstate.Flags) (*state.TaskSet, error) { - t1 := s.NewTask("fake-install-snap", name) - t2 := s.NewTask("fake-install-snap", "second task is just here so that we can check that the wait is correctly added to all tasks") - installQueue = append(installQueue, t1, t2) - return state.NewTaskSet(t1, t2), nil - } - - d := s.daemon(c) - - d.overlord.Loop() - defer d.overlord.Stop() - - buf := bytes.NewBufferString(`{"action": "install"}`) - req, err := http.NewRequest("POST", "/v2/snaps/some-snap", buf) - c.Assert(err, check.IsNil) - - s.vars = map[string]string{"name": "some-snap"} - rsp := postSnap(snapCmd, req, nil).(*resp) - - c.Assert(rsp.Type, check.Equals, ResponseTypeAsync) - - st := d.overlord.State() - st.Lock() - defer st.Unlock() - chg := st.Change(rsp.Change) - c.Assert(chg, check.NotNil) - - c.Check(chg.Tasks(), check.HasLen, 4) - - c.Check(installQueue, check.HasLen, 4) - // the two OS snap install tasks - c.Check(installQueue[0].Summary(), check.Equals, defaultCoreSnapName) - c.Check(installQueue[0].WaitTasks(), check.HasLen, 0) - c.Check(installQueue[1].WaitTasks(), check.HasLen, 0) - // the two "some-snap" install tasks - c.Check(installQueue[2].Summary(), check.Equals, "some-snap") - c.Check(installQueue[2].WaitTasks(), check.HasLen, 2) - c.Check(installQueue[3].WaitTasks(), check.HasLen, 2) -} - -// Installing ubuntu-core when not having ubuntu-core doesn't misbehave and try -// to install ubuntu-core twice. -func (s *apiSuite) TestInstallCoreSnapWhenMissing(c *check.C) { - installQueue := []*state.Task{} - - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - // pretend we do not have a core - return nil, state.ErrNoState - } - snapstateInstall = func(s *state.State, name, channel string, revision snap.Revision, userID int, flags snapstate.Flags) (*state.TaskSet, error) { - t1 := s.NewTask("fake-install-snap", name) - t2 := s.NewTask("fake-install-snap", "second task is just here so that we can check that the wait is correctly added to all tasks") - installQueue = append(installQueue, t1, t2) - return state.NewTaskSet(t1, t2), nil - } - - d := s.daemon(c) - inst := &snapInstruction{ - Action: "install", - Snaps: []string{defaultCoreSnapName}, - } - - st := d.overlord.State() - st.Lock() - defer st.Unlock() - _, _, err := inst.dispatch()(inst, st) - c.Check(err, check.IsNil) - - c.Check(installQueue, check.HasLen, 2) - // the only OS snap install tasks - c.Check(installQueue[0].Summary(), check.Equals, defaultCoreSnapName) - c.Check(installQueue[0].WaitTasks(), check.HasLen, 0) - c.Check(installQueue[1].WaitTasks(), check.HasLen, 0) -} - func (s *apiSuite) TestInstallFails(c *check.C) { - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - // we have core - return nil, nil - } - snapstateInstall = func(s *state.State, name, channel string, revision snap.Revision, userID int, flags snapstate.Flags) (*state.TaskSet, error) { t := s.NewTask("fake-install-snap-error", "Install task") return state.NewTaskSet(t), nil } - d := s.daemon(c) - - d.overlord.Loop() - defer d.overlord.Stop() + d := s.daemonWithFakeSnapManager(c) buf := bytes.NewBufferString(`{"action": "install"}`) req, err := http.NewRequest("POST", "/v2/snaps/hello-world", buf) @@ -3214,7 +3119,7 @@ c.Check(chg.Tasks(), check.HasLen, 1) st.Unlock() - <-chg.Ready() + s.waitTrivialChange(c, chg) st.Lock() c.Check(chg.Err(), check.ErrorMatches, `(?sm).*Install task \(fake-install-snap-error errored\)`) @@ -3250,10 +3155,6 @@ func (s *apiSuite) TestInstallDevMode(c *check.C) { var calledFlags snapstate.Flags - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - return nil, nil - } - snapstateInstall = func(s *state.State, name, channel string, revision snap.Revision, userID int, flags snapstate.Flags) (*state.TaskSet, error) { calledFlags = flags @@ -3281,10 +3182,6 @@ func (s *apiSuite) TestInstallJailMode(c *check.C) { var calledFlags snapstate.Flags - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - return nil, nil - } - snapstateInstall = func(s *state.State, name, channel string, revision snap.Revision, userID int, flags snapstate.Flags) (*state.TaskSet, error) { calledFlags = flags @@ -3657,9 +3554,6 @@ s.mockSnap(c, consumerYaml) s.mockSnap(c, differentProducerYaml) - d.overlord.Loop() - defer d.overlord.Stop() - action := &interfaceAction{ Action: "connect", Plugs: []plugJSON{{Snap: "consumer", Name: "plug"}}, @@ -3699,9 +3593,6 @@ s.mockSnap(c, producerYaml) s.mockSnap(c, consumerYaml) - d.overlord.Loop() - defer d.overlord.Stop() - action := &interfaceAction{ Action: "connect", Plugs: []plugJSON{{Snap: "consumer", Name: "missingplug"}}, @@ -3741,9 +3632,6 @@ s.mockSnap(c, producerYaml) // there is no producer, no slot defined - d.overlord.Loop() - defer d.overlord.Stop() - action := &interfaceAction{ Action: "connect", Plugs: []plugJSON{{Snap: "consumer", Name: "plug"}}, @@ -3842,15 +3730,12 @@ } func (s *apiSuite) TestDisconnectPlugFailureNoSuchPlug(c *check.C) { - d := s.daemon(c) + s.daemon(c) s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) // there is no consumer, no plug defined s.mockSnap(c, producerYaml) - d.overlord.Loop() - defer d.overlord.Stop() - action := &interfaceAction{ Action: "disconnect", Plugs: []plugJSON{{Snap: "consumer", Name: "plug"}}, @@ -3878,15 +3763,12 @@ } func (s *apiSuite) TestDisconnectPlugFailureNoSuchSlot(c *check.C) { - d := s.daemon(c) + s.daemon(c) s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) s.mockSnap(c, consumerYaml) // there is no producer, no slot defined - d.overlord.Loop() - defer d.overlord.Stop() - action := &interfaceAction{ Action: "disconnect", Plugs: []plugJSON{{Snap: "consumer", Name: "plug"}}, @@ -3915,15 +3797,12 @@ } func (s *apiSuite) TestDisconnectPlugFailureNotConnected(c *check.C) { - d := s.daemon(c) + s.daemon(c) s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) - d.overlord.Loop() - defer d.overlord.Stop() - action := &interfaceAction{ Action: "disconnect", Plugs: []plugJSON{{Snap: "consumer", Name: "plug"}}, @@ -5742,10 +5621,6 @@ func (s *apiSuite) TestInstallUnaliased(c *check.C) { var calledFlags snapstate.Flags - snapstateCoreInfo = func(s *state.State) (*snap.Info, error) { - return nil, nil - } - snapstateInstall = func(s *state.State, name, channel string, revision snap.Revision, userID int, flags snapstate.Flags) (*state.TaskSet, error) { calledFlags = flags @@ -5785,9 +5660,7 @@ } func (s *postDebugSuite) TestPostDebugEnsureStateSoon(c *check.C) { - d := s.daemon(c) - d.overlord.Loop() - defer d.overlord.Stop() + s.daemonWithOverlordMock(c) soon := 0 ensureStateSoon = func(st *state.State) { @@ -5882,13 +5755,13 @@ rsp := getAppsInfo(appsCmd, req, nil).(*resp) c.Assert(rsp.Status, check.Equals, 200) c.Assert(rsp.Type, check.Equals, ResponseTypeSync) - c.Assert(rsp.Result, check.FitsTypeOf, []*client.AppInfo{}) - apps := rsp.Result.([]*client.AppInfo) + c.Assert(rsp.Result, check.FitsTypeOf, []client.AppInfo{}) + apps := rsp.Result.([]client.AppInfo) c.Assert(apps, check.HasLen, 6) for _, name := range svcNames { snap, app := splitAppName(name) - c.Check(apps, testutil.DeepContains, &client.AppInfo{ + c.Check(apps, testutil.DeepContains, client.AppInfo{ Snap: snap, Name: app, Daemon: "simple", @@ -5899,7 +5772,7 @@ for _, name := range []string{"snap-b.cmd1", "snap-d.cmd2", "snap-d.cmd3"} { snap, app := splitAppName(name) - c.Check(apps, testutil.DeepContains, &client.AppInfo{ + c.Check(apps, testutil.DeepContains, client.AppInfo{ Snap: snap, Name: app, }) @@ -5920,13 +5793,13 @@ rsp := getAppsInfo(appsCmd, req, nil).(*resp) c.Assert(rsp.Status, check.Equals, 200) c.Assert(rsp.Type, check.Equals, ResponseTypeSync) - c.Assert(rsp.Result, check.FitsTypeOf, []*client.AppInfo{}) - apps := rsp.Result.([]*client.AppInfo) + c.Assert(rsp.Result, check.FitsTypeOf, []client.AppInfo{}) + apps := rsp.Result.([]client.AppInfo) c.Assert(apps, check.HasLen, 2) for _, name := range []string{"snap-d.cmd2", "snap-d.cmd3"} { snap, app := splitAppName(name) - c.Check(apps, testutil.DeepContains, &client.AppInfo{ + c.Check(apps, testutil.DeepContains, client.AppInfo{ Snap: snap, Name: app, }) @@ -5956,13 +5829,13 @@ rsp := getAppsInfo(appsCmd, req, nil).(*resp) c.Assert(rsp.Status, check.Equals, 200) c.Assert(rsp.Type, check.Equals, ResponseTypeSync) - c.Assert(rsp.Result, check.FitsTypeOf, []*client.AppInfo{}) - svcs := rsp.Result.([]*client.AppInfo) + c.Assert(rsp.Result, check.FitsTypeOf, []client.AppInfo{}) + svcs := rsp.Result.([]client.AppInfo) c.Assert(svcs, check.HasLen, 3) for _, name := range svcNames { snap, app := splitAppName(name) - c.Check(svcs, testutil.DeepContains, &client.AppInfo{ + c.Check(svcs, testutil.DeepContains, client.AppInfo{ Snap: snap, Name: app, Daemon: "simple", @@ -6118,6 +5991,24 @@ c.Assert(appInfos, check.IsNil) } +func (s *apiSuite) TestLogsNoServices(c *check.C) { + // NOTE this is *apiSuite, not *appSuite, so there are no + // installed snaps with services + + cmd := testutil.MockCommand(c, "systemctl", "").Also("journalctl", "") + defer cmd.Restore() + s.daemon(c) + s.d.overlord.Loop() + defer s.d.overlord.Stop() + + req, err := http.NewRequest("GET", "/v2/logs", nil) + c.Assert(err, check.IsNil) + + rsp := getLogs(logsCmd, req, nil).(*resp) + c.Assert(rsp.Status, check.Equals, 404) + c.Assert(rsp.Type, check.Equals, ResponseTypeError) +} + func (s *appSuite) TestLogs(c *check.C) { s.jctlRCs = []io.ReadCloser{ioutil.NopCloser(strings.NewReader(` {"MESSAGE": "hello1", "SYSLOG_IDENTIFIER": "xyzzy", "_PID": "42", "__REALTIME_TIMESTAMP": "42"} @@ -6234,7 +6125,7 @@ c.Assert(rsp.Type, check.Equals, ResponseTypeError) } -func (s *appSuite) testPostApps(c *check.C, inst appInstruction, systemctlCall []string) *state.Change { +func (s *appSuite) testPostApps(c *check.C, inst servicestate.Instruction, systemctlCall []string) *state.Change { postBody, err := json.Marshal(inst) c.Assert(err, check.IsNil) @@ -6262,55 +6153,61 @@ } func (s *appSuite) TestPostAppsStartOne(c *check.C) { - inst := appInstruction{Action: "start", Names: []string{"snap-a.svc2"}} + inst := servicestate.Instruction{Action: "start", Names: []string{"snap-a.svc2"}} expected := []string{"systemctl", "start", "snap.snap-a.svc2.service"} s.testPostApps(c, inst, expected) } func (s *appSuite) TestPostAppsStartTwo(c *check.C) { - inst := appInstruction{Action: "start", Names: []string{"snap-a"}} + inst := servicestate.Instruction{Action: "start", Names: []string{"snap-a"}} expected := []string{"systemctl", "start", "snap.snap-a.svc1.service", "snap.snap-a.svc2.service"} chg := s.testPostApps(c, inst, expected) + chg.State().Lock() + defer chg.State().Unlock() // check the summary expands the snap into actual apps - c.Check(chg.Summary(), check.Equals, "start of [snap-a.svc1 snap-a.svc2]") + c.Check(chg.Summary(), check.Equals, "Running service command") + c.Check(chg.Tasks()[0].Summary(), check.Equals, "start of [snap-a.svc1 snap-a.svc2]") } func (s *appSuite) TestPostAppsStartThree(c *check.C) { - inst := appInstruction{Action: "start", Names: []string{"snap-a", "snap-b"}} + inst := servicestate.Instruction{Action: "start", Names: []string{"snap-a", "snap-b"}} expected := []string{"systemctl", "start", "snap.snap-a.svc1.service", "snap.snap-a.svc2.service", "snap.snap-b.svc3.service"} chg := s.testPostApps(c, inst, expected) // check the summary expands the snap into actual apps - c.Check(chg.Summary(), check.Equals, "start of [snap-a.svc1 snap-a.svc2 snap-b.svc3]") + c.Check(chg.Summary(), check.Equals, "Running service command") + chg.State().Lock() + defer chg.State().Unlock() + c.Check(chg.Tasks()[0].Summary(), check.Equals, "start of [snap-a.svc1 snap-a.svc2 snap-b.svc3]") } func (s *appSuite) TestPosetAppsStop(c *check.C) { - inst := appInstruction{Action: "stop", Names: []string{"snap-a.svc2"}} + inst := servicestate.Instruction{Action: "stop", Names: []string{"snap-a.svc2"}} expected := []string{"systemctl", "stop", "snap.snap-a.svc2.service"} s.testPostApps(c, inst, expected) } func (s *appSuite) TestPosetAppsRestart(c *check.C) { - inst := appInstruction{Action: "restart", Names: []string{"snap-a.svc2"}} + inst := servicestate.Instruction{Action: "restart", Names: []string{"snap-a.svc2"}} expected := []string{"systemctl", "restart", "snap.snap-a.svc2.service"} s.testPostApps(c, inst, expected) } func (s *appSuite) TestPosetAppsReload(c *check.C) { - inst := appInstruction{Action: "restart", Names: []string{"snap-a.svc2"}} + inst := servicestate.Instruction{Action: "restart", Names: []string{"snap-a.svc2"}} inst.Reload = true expected := []string{"systemctl", "reload-or-restart", "snap.snap-a.svc2.service"} s.testPostApps(c, inst, expected) } func (s *appSuite) TestPosetAppsEnableNow(c *check.C) { - inst := appInstruction{Action: "start", Names: []string{"snap-a.svc2"}} + inst := servicestate.Instruction{Action: "start", Names: []string{"snap-a.svc2"}} inst.Enable = true expected := []string{"systemctl", "enable", "--now", "snap.snap-a.svc2.service"} s.testPostApps(c, inst, expected) } func (s *appSuite) TestPosetAppsDisableNow(c *check.C) { - inst := appInstruction{Action: "stop", Names: []string{"snap-a.svc2"}} + inst := servicestate.Instruction{Action: "stop", Names: []string{"snap-a.svc2"}} inst.Disable = true expected := []string{"systemctl", "disable", "--now", "snap.snap-a.svc2.service"} s.testPostApps(c, inst, expected) @@ -6381,7 +6278,7 @@ req, err := http.NewRequest("POST", "/v2/apps", bytes.NewBufferString(`{"action": "start", "names": ["snap-a.svc1"]}`)) c.Assert(err, check.IsNil) rsp := postApps(appsCmd, req, nil).(*resp) - c.Check(rsp.Status, check.Equals, 500) + c.Check(rsp.Status, check.Equals, 400) c.Check(rsp.Type, check.Equals, ResponseTypeError) c.Check(rsp.Result.(*errorResult).Message, check.Equals, `snap "snap-a" has changes in progress`) } diff -Nru snapd-2.28.5/daemon/daemon.go snapd-2.29.3/daemon/daemon.go --- snapd-2.28.5/daemon/daemon.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/daemon/daemon.go 2017-10-27 06:08:37.000000000 +0000 @@ -39,7 +39,7 @@ "github.com/snapcore/snapd/client" "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/httputil" - "github.com/snapcore/snapd/i18n/dumb" + "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/overlord" @@ -86,12 +86,20 @@ d *Daemon } +type accessResult int + +const ( + accessOK accessResult = iota + accessUnauthorized + accessForbidden +) + var polkitCheckAuthorizationForPid = polkit.CheckAuthorizationForPid -func (c *Command) canAccess(r *http.Request, user *auth.UserState) bool { +func (c *Command) canAccess(r *http.Request, user *auth.UserState) accessResult { if user != nil { // Authenticated users do anything for now. - return true + return accessOK } isUser := false @@ -100,30 +108,30 @@ isUser = true } else if err != errNoID { logger.Noticef("unexpected error when attempting to get UID: %s", err) - return false + return accessForbidden } else if c.SnapOK { - return true + return accessOK } if r.Method == "GET" { // Guest and user access restricted to GET requests if c.GuestOK { - return true + return accessOK } if isUser && c.UserOK { - return true + return accessOK } } // Remaining admin checks rely on identifying peer uid if !isUser { - return false + return accessUnauthorized } if uid == 0 { // Superuser does anything. - return true + return accessOK } if c.PolkitOK != "" { @@ -139,14 +147,16 @@ if authorized, err := polkitCheckAuthorizationForPid(pid, c.PolkitOK, nil, flags); err == nil { if authorized { // polkit says user is authorised - return true + return accessOK } - } else if err != polkit.ErrDismissed { + } else if err == polkit.ErrDismissed { + return accessForbidden + } else { logger.Noticef("polkit error: %s", err) } } - return false + return accessUnauthorized } func (c *Command) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -156,9 +166,15 @@ user, _ := UserFromRequest(state, r) state.Unlock() - if !c.canAccess(r, user) { + switch c.canAccess(r, user) { + case accessOK: + // nothing + case accessUnauthorized: Unauthorized("access denied").ServeHTTP(w, r) return + case accessForbidden: + Forbidden("forbidden").ServeHTTP(w, r) + return } var rspf ResponseFunc diff -Nru snapd-2.28.5/daemon/daemon_test.go snapd-2.29.3/daemon/daemon_test.go --- snapd-2.28.5/daemon/daemon_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/daemon/daemon_test.go 2017-10-27 06:08:37.000000000 +0000 @@ -41,7 +41,6 @@ "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/overlord/auth" - "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" "github.com/snapcore/snapd/polkit" "github.com/snapcore/snapd/testutil" @@ -63,10 +62,6 @@ return s.authorized, s.err } -func (s *daemonSuite) SetUpSuite(c *check.C) { - snapstate.CanAutoRefresh = nil -} - func (s *daemonSuite) SetUpTest(c *check.C) { dirs.SetRootDir(c.MkDir()) err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755) @@ -152,31 +147,31 @@ del := &http.Request{Method: "DELETE"} cmd := &Command{d: newTestDaemon(c)} - c.Check(cmd.canAccess(get, nil), check.Equals, false) - c.Check(cmd.canAccess(put, nil), check.Equals, false) - c.Check(cmd.canAccess(pst, nil), check.Equals, false) - c.Check(cmd.canAccess(del, nil), check.Equals, false) + c.Check(cmd.canAccess(get, nil), check.Equals, accessUnauthorized) + c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized) + c.Check(cmd.canAccess(pst, nil), check.Equals, accessUnauthorized) + c.Check(cmd.canAccess(del, nil), check.Equals, accessUnauthorized) cmd = &Command{d: newTestDaemon(c), UserOK: true} - c.Check(cmd.canAccess(get, nil), check.Equals, false) - c.Check(cmd.canAccess(put, nil), check.Equals, false) - c.Check(cmd.canAccess(pst, nil), check.Equals, false) - c.Check(cmd.canAccess(del, nil), check.Equals, false) + c.Check(cmd.canAccess(get, nil), check.Equals, accessUnauthorized) + c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized) + c.Check(cmd.canAccess(pst, nil), check.Equals, accessUnauthorized) + c.Check(cmd.canAccess(del, nil), check.Equals, accessUnauthorized) cmd = &Command{d: newTestDaemon(c), GuestOK: true} - c.Check(cmd.canAccess(get, nil), check.Equals, true) - c.Check(cmd.canAccess(put, nil), check.Equals, false) - c.Check(cmd.canAccess(pst, nil), check.Equals, false) - c.Check(cmd.canAccess(del, nil), check.Equals, false) + c.Check(cmd.canAccess(get, nil), check.Equals, accessOK) + c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized) + c.Check(cmd.canAccess(pst, nil), check.Equals, accessUnauthorized) + c.Check(cmd.canAccess(del, nil), check.Equals, accessUnauthorized) // Since this request has no RemoteAddr, it must be coming from the snap // socket instead of the snapd one. In that case, if SnapOK is true, this // command should be wide open for all HTTP methods. cmd = &Command{d: newTestDaemon(c), SnapOK: true} - c.Check(cmd.canAccess(get, nil), check.Equals, true) - c.Check(cmd.canAccess(put, nil), check.Equals, true) - c.Check(cmd.canAccess(pst, nil), check.Equals, true) - c.Check(cmd.canAccess(del, nil), check.Equals, true) + c.Check(cmd.canAccess(get, nil), check.Equals, accessOK) + c.Check(cmd.canAccess(put, nil), check.Equals, accessOK) + c.Check(cmd.canAccess(pst, nil), check.Equals, accessOK) + c.Check(cmd.canAccess(del, nil), check.Equals, accessOK) } func (s *daemonSuite) TestUserAccess(c *check.C) { @@ -184,23 +179,23 @@ put := &http.Request{Method: "PUT", RemoteAddr: "pid=100;uid=42;"} cmd := &Command{d: newTestDaemon(c)} - c.Check(cmd.canAccess(get, nil), check.Equals, false) - c.Check(cmd.canAccess(put, nil), check.Equals, false) + c.Check(cmd.canAccess(get, nil), check.Equals, accessUnauthorized) + c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized) cmd = &Command{d: newTestDaemon(c), UserOK: true} - c.Check(cmd.canAccess(get, nil), check.Equals, true) - c.Check(cmd.canAccess(put, nil), check.Equals, false) + c.Check(cmd.canAccess(get, nil), check.Equals, accessOK) + c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized) cmd = &Command{d: newTestDaemon(c), GuestOK: true} - c.Check(cmd.canAccess(get, nil), check.Equals, true) - c.Check(cmd.canAccess(put, nil), check.Equals, false) + c.Check(cmd.canAccess(get, nil), check.Equals, accessOK) + c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized) // Since this request has a RemoteAddr, it must be coming from the snapd // socket instead of the snap one. In that case, SnapOK should have no // bearing on the default behavior, which is to deny access. cmd = &Command{d: newTestDaemon(c), SnapOK: true} - c.Check(cmd.canAccess(get, nil), check.Equals, false) - c.Check(cmd.canAccess(put, nil), check.Equals, false) + c.Check(cmd.canAccess(get, nil), check.Equals, accessUnauthorized) + c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized) } func (s *daemonSuite) TestSuperAccess(c *check.C) { @@ -208,20 +203,20 @@ put := &http.Request{Method: "PUT", RemoteAddr: "pid=100;uid=0;"} cmd := &Command{d: newTestDaemon(c)} - c.Check(cmd.canAccess(get, nil), check.Equals, true) - c.Check(cmd.canAccess(put, nil), check.Equals, true) + c.Check(cmd.canAccess(get, nil), check.Equals, accessOK) + c.Check(cmd.canAccess(put, nil), check.Equals, accessOK) cmd = &Command{d: newTestDaemon(c), UserOK: true} - c.Check(cmd.canAccess(get, nil), check.Equals, true) - c.Check(cmd.canAccess(put, nil), check.Equals, true) + c.Check(cmd.canAccess(get, nil), check.Equals, accessOK) + c.Check(cmd.canAccess(put, nil), check.Equals, accessOK) cmd = &Command{d: newTestDaemon(c), GuestOK: true} - c.Check(cmd.canAccess(get, nil), check.Equals, true) - c.Check(cmd.canAccess(put, nil), check.Equals, true) + c.Check(cmd.canAccess(get, nil), check.Equals, accessOK) + c.Check(cmd.canAccess(put, nil), check.Equals, accessOK) cmd = &Command{d: newTestDaemon(c), SnapOK: true} - c.Check(cmd.canAccess(get, nil), check.Equals, true) - c.Check(cmd.canAccess(put, nil), check.Equals, true) + c.Check(cmd.canAccess(get, nil), check.Equals, accessOK) + c.Check(cmd.canAccess(put, nil), check.Equals, accessOK) } func (s *daemonSuite) TestPolkitAccess(c *check.C) { @@ -230,15 +225,19 @@ // polkit says user is not authorised s.authorized = false - c.Check(cmd.canAccess(put, nil), check.Equals, false) + c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized) // polkit grants authorisation s.authorized = true - c.Check(cmd.canAccess(put, nil), check.Equals, true) + c.Check(cmd.canAccess(put, nil), check.Equals, accessOK) // an error occurs communicating with polkit s.err = errors.New("error") - c.Check(cmd.canAccess(put, nil), check.Equals, false) + c.Check(cmd.canAccess(put, nil), check.Equals, accessUnauthorized) + + // if the user dismisses the auth request, forbid access + s.err = polkit.ErrDismissed + c.Check(cmd.canAccess(put, nil), check.Equals, accessForbidden) } func (s *daemonSuite) TestPolkitAccessForGet(c *check.C) { @@ -247,14 +246,14 @@ // polkit can grant authorisation for GET requests s.authorized = true - c.Check(cmd.canAccess(get, nil), check.Equals, true) + c.Check(cmd.canAccess(get, nil), check.Equals, accessOK) // for UserOK commands, polkit is not consulted cmd.UserOK = true polkitCheckAuthorizationForPid = func(pid uint32, actionId string, details map[string]string, flags polkit.CheckFlags) (bool, error) { panic("polkit.CheckAuthorizationForPid called") } - c.Check(cmd.canAccess(get, nil), check.Equals, true) + c.Check(cmd.canAccess(get, nil), check.Equals, accessOK) } func (s *daemonSuite) TestPolkitInteractivity(c *check.C) { @@ -267,18 +266,18 @@ c.Assert(err, check.IsNil) logger.SetLogger(log) - c.Check(cmd.canAccess(put, nil), check.Equals, true) + c.Check(cmd.canAccess(put, nil), check.Equals, accessOK) c.Check(s.lastPolkitFlags, check.Equals, polkit.CheckNone) c.Check(logbuf.String(), check.Equals, "") put.Header.Set(client.AllowInteractionHeader, "true") - c.Check(cmd.canAccess(put, nil), check.Equals, true) + c.Check(cmd.canAccess(put, nil), check.Equals, accessOK) c.Check(s.lastPolkitFlags, check.Equals, polkit.CheckAllowInteraction) c.Check(logbuf.String(), check.Equals, "") // bad values are logged and treated as false put.Header.Set(client.AllowInteractionHeader, "garbage") - c.Check(cmd.canAccess(put, nil), check.Equals, true) + c.Check(cmd.canAccess(put, nil), check.Equals, accessOK) c.Check(s.lastPolkitFlags, check.Equals, polkit.CheckNone) c.Check(logbuf.String(), testutil.Contains, "error parsing X-Allow-Interaction header:") } diff -Nru snapd-2.28.5/daemon/snap.go snapd-2.29.3/daemon/snap.go --- snapd-2.28.5/daemon/snap.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/daemon/snap.go 2017-11-02 15:44:20.000000000 +0000 @@ -163,13 +163,6 @@ return about, firstErr } -// screenshotJSON contains the json for snap.ScreenshotInfo -type screenshotJSON struct { - URL string `json:"url"` - Width int64 `json:"width,omitempty"` - Height int64 `json:"height,omitempty"` -} - type bySnapApp []*snap.AppInfo func (a bySnapApp) Len() int { return len(a) } @@ -282,14 +275,14 @@ return appInfos, nil } -func clientAppInfosFromSnapAppInfos(apps []*snap.AppInfo) []*client.AppInfo { +func clientAppInfosFromSnapAppInfos(apps []*snap.AppInfo) []client.AppInfo { // TODO: pass in an actual notifier here instead of null // (Status doesn't _need_ it, but benefits from it) - sysd := systemd.New(dirs.GlobalRootDir, &progress.NullProgress{}) + sysd := systemd.New(dirs.GlobalRootDir, progress.Null) - out := make([]*client.AppInfo, len(apps)) + out := make([]client.AppInfo, len(apps)) for i, app := range apps { - out[i] = &client.AppInfo{ + out[i] = client.AppInfo{ Snap: app.Snap.Name(), Name: app.Name, } @@ -314,7 +307,7 @@ return out } -func mapLocal(about aboutSnap) map[string]interface{} { +func mapLocal(about aboutSnap) *client.Snap { localSnap, snapst := about.info, about.snapst status := "installed" if snapst.Active && localSnap.Revision == snapst.Current { @@ -331,43 +324,38 @@ // TODO: expose aliases information and state? - result := map[string]interface{}{ - "description": localSnap.Description(), - "developer": about.publisher, - "icon": snapIcon(localSnap), - "id": localSnap.SnapID, - "install-date": snapDate(localSnap), - "installed-size": localSnap.Size, - "name": localSnap.Name(), - "revision": localSnap.Revision, - "status": status, - "summary": localSnap.Summary(), - "type": string(localSnap.Type), - "version": localSnap.Version, - "channel": localSnap.Channel, - "tracking-channel": snapst.Channel, - "confinement": localSnap.Confinement, - "devmode": snapst.DevMode, - "trymode": snapst.TryMode, - "jailmode": snapst.JailMode, - "private": localSnap.Private, - "apps": apps, - "broken": localSnap.Broken, - "contact": localSnap.Contact, - } - - if localSnap.Title() != "" { - result["title"] = localSnap.Title() - } - - if localSnap.License != "" { - result["license"] = localSnap.License + result := &client.Snap{ + Description: localSnap.Description(), + Developer: about.publisher, + Icon: snapIcon(localSnap), + ID: localSnap.SnapID, + InstallDate: snapDate(localSnap), + InstalledSize: localSnap.Size, + Name: localSnap.Name(), + Revision: localSnap.Revision, + Status: status, + Summary: localSnap.Summary(), + Type: string(localSnap.Type), + Version: localSnap.Version, + Channel: localSnap.Channel, + TrackingChannel: snapst.Channel, + // TODO: send ignore-validation + Confinement: string(localSnap.Confinement), + DevMode: snapst.DevMode, + TryMode: snapst.TryMode, + JailMode: snapst.JailMode, + Private: localSnap.Private, + Apps: apps, + Broken: localSnap.Broken, + Contact: localSnap.Contact, + Title: localSnap.Title(), + License: localSnap.License, } return result } -func mapRemote(remoteSnap *snap.Info) map[string]interface{} { +func mapRemote(remoteSnap *snap.Info) *client.Snap { status := "available" if remoteSnap.MustBuy { status = "priced" @@ -378,55 +366,37 @@ confinement = snap.StrictConfinement } - screenshots := make([]screenshotJSON, len(remoteSnap.Screenshots)) + screenshots := make([]client.Screenshot, len(remoteSnap.Screenshots)) for i, screenshot := range remoteSnap.Screenshots { - screenshots[i] = screenshotJSON{ + screenshots[i] = client.Screenshot{ URL: screenshot.URL, Width: screenshot.Width, Height: screenshot.Height, } } - result := map[string]interface{}{ - "description": remoteSnap.Description(), - "developer": remoteSnap.Publisher, - "download-size": remoteSnap.Size, - "icon": snapIcon(remoteSnap), - "id": remoteSnap.SnapID, - "name": remoteSnap.Name(), - "revision": remoteSnap.Revision, - "status": status, - "summary": remoteSnap.Summary(), - "type": string(remoteSnap.Type), - "version": remoteSnap.Version, - "channel": remoteSnap.Channel, - "private": remoteSnap.Private, - "confinement": confinement, - "contact": remoteSnap.Contact, - } - - if remoteSnap.Title() != "" { - result["title"] = remoteSnap.Title() - } - - if remoteSnap.License != "" { - result["license"] = remoteSnap.License - } - - if len(screenshots) > 0 { - result["screenshots"] = screenshots - } - - if len(remoteSnap.Prices) > 0 { - result["prices"] = remoteSnap.Prices - } - - if len(remoteSnap.Channels) > 0 { - result["channels"] = remoteSnap.Channels - } - - if len(remoteSnap.Tracks) > 0 { - result["tracks"] = remoteSnap.Tracks + result := &client.Snap{ + Description: remoteSnap.Description(), + Developer: remoteSnap.Publisher, + DownloadSize: remoteSnap.Size, + Icon: snapIcon(remoteSnap), + ID: remoteSnap.SnapID, + Name: remoteSnap.Name(), + Revision: remoteSnap.Revision, + Status: status, + Summary: remoteSnap.Summary(), + Type: string(remoteSnap.Type), + Version: remoteSnap.Version, + Channel: remoteSnap.Channel, + Private: remoteSnap.Private, + Confinement: string(confinement), + Contact: remoteSnap.Contact, + Title: remoteSnap.Title(), + License: remoteSnap.License, + Screenshots: screenshots, + Prices: remoteSnap.Prices, + Channels: remoteSnap.Channels, + Tracks: remoteSnap.Tracks, } return result diff -Nru snapd-2.28.5/daemon/ucrednet.go snapd-2.29.3/daemon/ucrednet.go --- snapd-2.28.5/daemon/ucrednet.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/daemon/ucrednet.go 2017-10-23 06:17:27.000000000 +0000 @@ -57,7 +57,8 @@ if pid == ucrednetNoProcess || uid == ucrednetNobody { err = errNoID } - return + + return pid, uid, err } type ucrednetAddr struct { diff -Nru snapd-2.28.5/data/completion/snap snapd-2.29.3/data/completion/snap --- snapd-2.28.5/data/completion/snap 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/data/completion/snap 2017-10-23 06:17:27.000000000 +0000 @@ -19,24 +19,34 @@ local cur prev words cword _init_completion -n : || return + if [[ ${#words[@]} -le 2 ]]; then + # we're completing on the first word + COMPREPLY=($(GO_FLAGS_COMPLETION=1 "${words[@]}")) + return 0 + fi + local command - if [[ ${#words[@]} -gt 2 ]]; then - if [[ ${words[1]} =~ ^-- ]]; then - # global options take no args - return 0 - fi - if [[ ${words[-2]} = "--help" ]]; then - # help takes no args + if [[ ${words[1]} =~ ^- ]]; then + # global options take no args + return 0 + fi + + for w in "${words[@]:1}"; do + if [[ "$w" == "-h" || "$w" == "--help" ]]; then + # completing on help gets confusing return 0 fi + done - command=${words[-2]} - fi + command=${words[1]} # Only split on newlines local IFS=$'\n' - COMPREPLY=($(GO_FLAGS_COMPLETION=1 "${words[@]}")) + # now we pass _just the bit that's being completed_ of the command + # to snap for it to figure it out. go-flags isn't smart enough to + # look at COMP_WORDS etc. itself. + COMPREPLY=($(GO_FLAGS_COMPLETION=1 snap "$command" "$cur")) case $command in install|info|sign-build) diff -Nru snapd-2.28.5/debian/changelog snapd-2.29.3/debian/changelog --- snapd-2.28.5/debian/changelog 2017-10-13 21:25:46.000000000 +0000 +++ snapd-2.29.3/debian/changelog 2017-11-09 18:16:29.000000000 +0000 @@ -1,3 +1,241 @@ +snapd (2.29.3) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - daemon: cherry-picked /v2/logs fixes + - cmd/snap-confine: Respect biarch nature of libdirs + - cmd/snap-confine: Ensure snap-confine is allowed to access os- + release + - interfaces: fix udev tagging for hooks + - cmd: fix re-exec bug with classic confinement for host snapd + - tests: disable xdg-open-compat test + - cmd/snap-confine: add slave PTYs and let devpts newinstance + perform mediation + - interfaces/many: misc policy updates for browser-support, cups- + control and network-status + - interfaces/raw-usb: match on SUBSYSTEM, not SUBSYSTEMS + - tests: fix security-device-cgroup* tests on devices with + framebuffer + + -- Michael Vogt Thu, 09 Nov 2017 19:16:29 +0100 + +snapd (2.29.2) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - snapctl: disable stop/start/restart (2.29) + - cmd/snap-update-ns: fix collection of changes made + + -- Michael Vogt Fri, 03 Nov 2017 17:17:14 +0100 + +snapd (2.29.1) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - interfaces: fix incorrect signature of ofono DBusPermanentSlot + - interfaces/serial-port: udev tag plugged slots that have just + 'path' via KERNEL + - interfaces/hidraw: udev tag plugged slots that have just 'path' + via KERNEL + - interfaces/uhid: unconditionally add existing uhid device to the + device cgroup + - cmd/snap-update-ns: fix mount rules for font sharing + - tests: disable refresh-undo test on trusty for now + - tests: use `snap change --last=install` in snapd-reexec test + - Revert " wrappers: fail install if exec-line cannot be re-written + - interfaces: don't udev tag devmode or classic snaps + - many: make ignore-validation sticky and send the flag with refresh + requests + + -- Michael Vogt Fri, 03 Nov 2017 07:25:17 +0100 + +snapd (2.29) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - interfaces/many: miscellaneous updates based on feedback from the + field + - snap-confine: allow reading uevents from any where in /sys + - spread: add bionic beaver + - debian: make packaging/ubuntu-14.04/copyright a real file again + - tests: cherry pick the fix for services test into 2.29 + - cmd/snap-update-ns: initialize logger + - hooks/configure: queue service restarts + - snap-{confine,seccomp}: make @unrestricted fully unrestricted + - interfaces: clean system apparmor cache on core device + - debian: do not build static snap-exec on powerpc + - snap-confine: increase sanity_timeout to 6s + - snapctl: cherry pick service commands changes + - cmd/snap: tell translators about arg names and descs req's + - systemd: run all mount units before snapd.service to avoid race + - store: add a test to show auth failures are forwarded by doRequest + - daemon: convert ErrInvalidCredentials to a 401 Unauthorized error. + - store: forward on INVALID_CREDENTIALS error as + ErrInvalidCredentials + - daemon: generate a forbidden response message if polkit dialog is + dismissed + - daemon: Allow Polkit authorization to cancel changes. + - travis: switch to container based test runs + - interfaces: reduce duplicated code in interface tests mocks + - tests: improve revert related testing + - interfaces: sanitize plugs and slots early in ReadInfo + - store: add download caching + - preserve TMPDIR and HOSTALIASES across snap-confine invocation + - snap-confine: init all arrays with `= {0,}` + - tests: adding test for network-manager interface + - interfaces/mount: don't generate legacy per-hook/per-app mount + profiles + - snap: introduce structured epochs + - tests: fix interfaces-cups-control test for cups-2.2.5 + - snap-confine: cleanup incorrectly created nvidia udev tags + - cmd/snap-confine: update valid security tag regexp + - cmd/libsnap: enable two stranded tests + - cmd,packaging: enable apparmor on openSUSE + - overlord/ifacestate: refresh all security backends on startup + - interfaces/dbus: drop unneeded check for + release.ReleaseInfo.ForceDevMode + - dbus: ensure io.snapcraft.Launcher.service is created on re- + exec + - overlord/auth: continue for now supporting UBUNTU_STORE_ID if the + model is generic-classic + - snap-confine: add support for handling /dev/nvidia-modeset + - interfaces/network-control: remove incorrect rules for tun + - spread: allow setting SPREAD_DEBUG_EACH=0 to disable debug-each + section + - packaging: remove .mnt files on removal + - tests: fix econnreset scenario when the iptables rule was not + created + - tests: add test for lxd interface + - run-checks: use nakedret static checker to check for naked + returns on long functions + - progress: be more flexible in testing ansimeter + - interfaces: fix udev rules for tun + - many: implement our own ANSI-escape-using progress indicator + - snap-exec: update tests to follow main_test pattern + - snap: support "command: foo $ENV_STRING" + - packaging: update nvidia configure options + - snap: add new `snap pack` and use in tests + - cmd: correctly name the "Ubuntu" and "Arch" NVIDIA methods + - cmd: add autogen case for solus + - tests: do not use http://canihazip.com/ which appears to be down + - hooks: commands for controlling own services from snapctl + - snap: refactor cmdGet.Execute() + - interfaces/mount: make Change.Perform testable and test it + - interfaces/mount,cmd/snap-update-ns: move change code + - snap-confine: is_running_on_classic_distribution() looks into os- + release + - interfaces: misc updates for default, browser-support, home and + system-observe + - interfaces: deny lttng by default + - interfaces/lxd: lxd slot implementation can also be an app snap + - release,cmd,dirs: Redo the distro checks to take into account + distribution families + - cmd/snap: completion for alias and unalias + - snap-confine: add new SC_CLEANUP and use it + - snap: refrain from running filepath.Base on random strings + - cmd/snap-confine: put processes into freezer hierarchy + - wrappers: fail install if exec-line cannot be re-written + - cmd/snap-seccomp,osutil: make user/group lookup functions public + - snapstate: deal with snap user data in the /root/ directory + - interfaces: Enhance full-confinement support for biarch + distributions + - snap-confine: Only attempt to copy/mount NVIDIA libs when NVIDIA + is used + - packaging/fedora: Add Fedora 26, 27, and Rawhide symlinks + - overlord/snapstate: prefer a smaller corner case for doing the + wrong thing + - cmd/snap-repair: set user agent for snap-repair http requests + - packaging: bring down the delta between 14.04 and 16.04 + - snap-confine: Ensure lib64 biarch directory is respected + - snap-confine: update apparmor rules for fedora based base snaps + - tests: Increase SNAPD_CONFIGURE_HOOK_TIMEOUT to 3 minutes to + install real snaps + - daemon: use client.Snap instead of map[string]interface{} for + snaps. + - hooks: rename refresh hook to post-refresh + - git: make the .gitingore file a bit more targeted + - interfaces/opengl: don't udev tag nvidia devices and use snap- + confine instead + - cmd/snap-{confine,update-ns}: apply mount profiles using snap- + update-ns + - cmd: update "make hack" + - interfaces/system-observe: allow clients to enumerate DBus + connection names + - snap-repair: implement `snap-repair {list,show}` + - dirs,interfaces: create snap-confine.d on demand when re-executing + - snap-confine: fix base snaps on core + - cmd/snap-repair: fix tests when running as root + - interfaces: add Connection type + - cmd/snap-repair: skip disabled repairs + - cmd/snap-repair: prefer leaking unmanaged fds on test failure over + closing random ones + - snap-repair: make `repair` binary available for repair scripts + - snap-repair: fix missing Close() in TestStatusHappy + - cmd/snap-confine,packaging: import snapd-generated policy + - cmd/snap: return empty document if snap has no configuration + - snap-seccomp: run secondary-arch tests via gcc-multilib + - snap: implement `snap {repair,repairs}` and pass-through to snap- + repair + - interfaces/builtin: allow receiving dbus messages + - snap-repair: implement `snap-repair {done,skip,retry}` + - data/completion: small tweak to snap completion snippet + - dirs: fix classic support detection + - cmd/snap-repair: integrate root public keys for repairs + - tests: fix ubuntu core services + - tests: add new test that checks that the compat snapd-xdg-open + works + - snap-confine: improve error message if core/u-core cannot be found + - tests: only run tests/regression/nmcli on amd64 + - interfaces: mount host system fonts in desktop interface + - interfaces: enable partial apparmor support + - snapstate: auto-install missing base snaps + - spread: work around temporary packaging issue in debian sid + - asserts,cmd/snap-repair: introduce a mandatory summary for repairs + - asserts,cmd/snap-repair: represent RepairID internally as an int + - tests: test the real "xdg-open" from the core snap + - many: implement fetching sections and package names periodically. + - interfaces/network: allow using netcat as client + - snap-seccomp, osutil: use osutil.AtomicFile in snap-seccomp + - snap-seccomp: skip mknod syscall on arm64 + - tests: add trivial canonical-livepatch test + - tests: add test that ensures that all core services are working + - many: add logger.MockLogger() and use it in the tests + - snap-repair: fix test failure in TestRepairHitsTimeout + - asserts: add empty values check in HeadersFromPrimaryKey + - daemon: remove unused installSnap var in test + - daemon: reach for Overlord.Loop less thanks to overlord.Mock + - snap-seccomp: manually resolve socket() call in tests + - tests: change regex used to validate installed ubuntu core snap + - cmd/snapctl: allow snapctl -h without a context (regression fix). + - many: use snapcore/snapd/i18n instead of i18n/dumb + - many: introduce asserts.NotFoundError replacing both ErrNotFound + and store.AssertionNotFoundError + - packaging: don't include any marcos in comments + - overlord: use overlord.Mock in more tests, make sure we check the + outcome of Settle + - tests: try to fix staging tests + - store: simplify api base url config + - systemd: add systemd.MockJournalctl() + - many: provide systemd.MockSystemctl() helper + - tests: improve the listing test to not fail for e.g. 2.28~rc2 + - snapstate: give snapmgrTestSuite.settle() more time to settle + - tests: fix regex to check core version on snap list + - debian: update trusted account-keys check on 14.04 packaging + - interfaces: add udev netlink support to hardware-observe + - overlord: introduce Mock which enables to use Overlord.Settle for + settle in many more places + - snap-repair: execute the repair and capture logs/status + - tests: run the tests/unit/go everywhere + - daemon, snapstate: move ensureCore from daemon/api.go into + snapstate.go + - cmd/snap: get keys or root document + - spread.yaml: turn suse to manual given that it's breaking master + - many: configure store from state, reconfigure store at runtime + - osutil: AtomicWriter (an io.Writer), and io.Reader versions of + AtomicWrite* + - tests: check for negative syscalls in runBpf() and skip those + tests + - docs: use abolute path in PULL_REQUEST_TEMPLATE.md + - store: move device auth endpoint uris to config (#3831) + + -- Michael Vogt Mon, 30 Oct 2017 16:22:31 +0100 + snapd (2.28.5) xenial; urgency=medium * New upstream release, LP: #1714984 diff -Nru snapd-2.28.5/debian/control snapd-2.29.3/debian/control --- snapd-2.28.5/debian/control 2017-10-11 17:40:25.000000000 +0000 +++ snapd-2.29.3/debian/control 2017-10-23 06:17:27.000000000 +0000 @@ -13,6 +13,7 @@ dh-golang (>=1.7), dh-systemd, fakeroot, + gcc-multilib [amd64], gettext, grub-common, gnupg2, diff -Nru snapd-2.28.5/debian/rules snapd-2.29.3/debian/rules --- snapd-2.28.5/debian/rules 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/debian/rules 2017-10-27 12:24:38.000000000 +0000 @@ -70,7 +70,7 @@ # for stability, predicability and easy of deployment. We need to link some # things dynamically though: udev has no stable IPC protocol between # libudev and udevd so we need to link with it dynamically. - VENDOR_ARGS=--enable-nvidia-ubuntu --enable-static-libcap --enable-static-libapparmor --enable-static-libseccomp + VENDOR_ARGS=--enable-nvidia-multiarch --enable-static-libcap --enable-static-libapparmor --enable-static-libseccomp BUILT_USING_PACKAGES=libcap-dev libapparmor-dev libseccomp-dev else ifeq ($(shell dpkg-vendor --query Vendor),Debian) @@ -124,12 +124,19 @@ mkdir -p _build/src/$(DH_GOPKG)/cmd/snap/test-data cp -a cmd/snap/test-data/*.gpg _build/src/$(DH_GOPKG)/cmd/snap/test-data/ dh_auto_build -- $(BUILDFLAGS) $(TAGS) $(GCCGOFLAGS) - # Generate static snap-exec - it somehow includes CGO so we must - # force a static build here. We need a static snap-exec inside - # the core snap because not all bases will have a libc + + # (static linking on powerpc with cgo is broken) +ifneq ($(shell dpkg-architecture -qDEB_HOST_ARCH),powerpc) + # Generate static snap-exec and snap-update-ns - it somehow includes CGO so + # we must force a static build here. We need a static snap-{exec,update-ns} + # inside the core snap because not all bases will have a libc (cd _build/bin && GOPATH=$$(pwd)/.. CGO_ENABLED=0 go build $(GCCGOFLAGS) $(DH_GOPKG)/cmd/snap-exec) + (cd _build/bin && GOPATH=$$(pwd)/.. go build --ldflags '-extldflags "-static"' $(GCCGOFLAGS) $(DH_GOPKG)/cmd/snap-update-ns) + # ensure we generated a static build $(shell if ldd _build/bin/snap-exec; then false "need static build"; fi) + $(shell if ldd _build/bin/snap-update-ns; then false "need static build"; fi) +endif # Build C bits, sadly manually cd cmd && ( autoreconf -i -f ) @@ -147,74 +154,19 @@ [ $$(strings _build/bin/snapd|grep -c -E "public-key-sha3-384: [a-zA-Z0-9_-]{64}") -eq 2 ] strings _build/bin/snapd|grep -c "^public-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk$$" strings _build/bin/snapd|grep -c "^public-key-sha3-384: d-JcZF9nD9eBw7bwMnH61x-bklnQOhQud1Is6o_cn2wTj8EYDi9musrIT9z2MdAa$$" + # same for snap-repair + [ $$(strings _build/bin/snap-repair|grep -c -E "public-key-sha3-384: [a-zA-Z0-9_-]{64}") -eq 3 ] + # common with snapd + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk$$" + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: d-JcZF9nD9eBw7bwMnH61x-bklnQOhQud1Is6o_cn2wTj8EYDi9musrIT9z2MdAa$$" + # repair-root + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: nttW6NfBXI_E-00u38W-KH6eiksfQNXuI7IiumoV49_zkbhM0sYTzSnFlwZC-W4t$$" endif ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) # run the snap-confine tests $(MAKE) -C cmd check endif -override_dh_systemd_enable: - # enable auto-import - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.autoimport.service - # we want the auto-update timer enabled by default - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.refresh.timer - # but the auto-update service disabled - dh_systemd_enable \ - --no-enable \ - -psnapd \ - data/systemd/snapd.refresh.service - # we want the repair timer enabled by default - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.snap-repair.timer - # but the repair service disabled - dh_systemd_enable \ - --no-enable \ - -psnapd \ - data/systemd/snapd.snap-repair.service - # enable snapd - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.socket - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.service - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.system-shutdown.service - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.core-fixup.service - -override_dh_systemd_start: - # we want to start the auto-update timer - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.refresh.timer - # but not start the service - dh_systemd_start \ - --no-start \ - -psnapd \ - data/systemd/snapd.refresh.service - # start snapd - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.socket - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.service - # start autoimport - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.autoimport.service - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.core-fixup.service - override_dh_install: # we do not need this in the package, its just needed during build rm -rf ${CURDIR}/debian/tmp/usr/bin/xgettext-go diff -Nru snapd-2.28.5/debian/snapd.dirs snapd-2.29.3/debian/snapd.dirs --- snapd-2.28.5/debian/snapd.dirs 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/debian/snapd.dirs 2017-10-23 06:17:27.000000000 +0000 @@ -1,5 +1,7 @@ snap +var/cache/snapd usr/lib/snapd +var/lib/snapd/apparmor/snap-confine.d var/lib/snapd/auto-import var/lib/snapd/desktop var/lib/snapd/environment diff -Nru snapd-2.28.5/debian/snapd.install snapd-2.29.3/debian/snapd.install --- snapd-2.28.5/debian/snapd.install 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/debian/snapd.install 2017-10-23 06:17:27.000000000 +0000 @@ -24,7 +24,7 @@ lib/udev/snappy-app-dev usr/lib/snapd/snap-confine usr/lib/snapd/snap-discard-ns -usr/share/man/man5/snap-confine.5 +usr/share/man/man1/snap-confine.1 usr/share/man/man5/snap-discard-ns.5 # for compatibility with ancient snap installs that wrote the shell based # wrapper scripts instead of the modern symlinks diff -Nru snapd-2.28.5/debian/snapd.postrm snapd-2.29.3/debian/snapd.postrm --- snapd-2.28.5/debian/snapd.postrm 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/debian/snapd.postrm 2017-10-23 06:17:27.000000000 +0000 @@ -4,22 +4,30 @@ systemctl_stop() { unit="$1" - if systemctl is-active -q "$unit"; then - echo "Stoping $unit" + for i in $(seq 10); do + if ! systemctl is-active -q "$unit"; then + echo "$unit is stopped." + break + fi + echo "Stoping $unit [attempt $i]" systemctl stop -q "$unit" || true - fi + sleep .1 + done } if [ "$1" = "purge" ]; then # undo any bind mount to /snap that resulted from LP:#1668659 + # (that bug can't happen in trusty -- and doing this would mess up snap.mount.service there) if grep -q "/snap /snap" /proc/self/mountinfo; then umount -l /snap || true fi - mounts=$(systemctl list-unit-files --full | grep '^snap[-.].*\.mount' | cut -f1 -d ' ') - services=$(systemctl list-unit-files --full | grep '^snap[-.].*\.service' | cut -f1 -d ' ') + units=$(systemctl list-unit-files --full | grep '^snap[-.]' | cut -f1 -d ' ' | grep -vF snap.mount.service || true) + mounts=$(echo "$units" | grep '^snap[-.].*\.mount$' || true) + services=$(echo "$units" | grep '^snap[-.].*\.service$' || true) + for unit in $services $mounts; do - # ensure its really a snapp mount unit or systemd unit + # ensure its really a snap mount unit or systemd unit if ! grep -q 'What=/var/lib/snapd/snaps/' "/etc/systemd/system/$unit" && ! grep -q 'X-Snappy=yes' "/etc/systemd/system/$unit"; then echo "Skipping non-snapd systemd unit $unit" continue @@ -43,7 +51,8 @@ rm -f "/snap/bin/$snap" rm -f "/snap/bin/$snap".* # snap mount dir - umount -l "/snap/$snap/$rev" 2> /dev/null || true + # we pass -d (clean up loopback devices) for trusty compatibility + umount -d -l "/snap/$snap/$rev" 2> /dev/null || true rm -rf "/snap/$snap/$rev" rm -f "/snap/$snap/current" # snap data dir @@ -53,7 +62,7 @@ # opportunistic remove (may fail if there are still revisions left for d in "/snap/$snap" "/var/snap/$snap"; do if [ -d "$d" ]; then - rmdir --ignore-fail-on-non-empty "$d" + rmdir --ignore-fail-on-non-empty "$d" || true fi done fi @@ -74,12 +83,19 @@ # opportunistic as those might not be actually mounted for mnt in /run/snapd/ns/*.mnt; do umount -l "$mnt" || true + rm -f "$mnt" + done + for fstab in /run/snapd/ns/*.fstab; do + rm -f "$fstab" done umount -l /run/snapd/ns/ || true echo "Removing extra snap-confine apparmor rules" rm -f /etc/apparmor.d/snap.core.*.usr.lib.snapd.snap-confine + echo "Removing snapd cache" + rm -f /var/cache/snapd/* + echo "Removing snapd state" rm -rf /var/lib/snapd fi diff -Nru snapd-2.28.5/dirs/dirs.go snapd-2.29.3/dirs/dirs.go --- snapd-2.28.5/dirs/dirs.go 2017-10-10 16:16:09.000000000 +0000 +++ snapd-2.29.3/dirs/dirs.go 2017-10-23 06:17:27.000000000 +0000 @@ -39,9 +39,11 @@ SnapBlobDir string SnapDataDir string SnapDataHomeGlob string + SnapDownloadCacheDir string SnapAppArmorDir string AppArmorCacheDir string SnapAppArmorAdditionalDir string + SnapAppArmorConfineDir string SnapSeccompDir string SnapMountPolicyDir string SnapUdevRulesDir string @@ -68,6 +70,11 @@ SnapRepairStateFile string SnapRepairRunDir string SnapRepairAssertsDir string + SnapRunRepairDir string + + SnapCacheDir string + SnapNamesFile string + SnapSectionsFile string SnapBinariesDir string SnapServicesDir string @@ -87,6 +94,10 @@ CompletionHelper string CompletersDir string CompleteSh string + + SystemFontsDir string + SystemLocalFontsDir string + SystemFontconfigCacheDir string ) const ( @@ -128,14 +139,14 @@ // SupportsClassicConfinement returns true if the current directory layout supports classic confinement. func SupportsClassicConfinement() bool { - if SnapMountDir == defaultSnapMountDir { + smd := filepath.Join(GlobalRootDir, defaultSnapMountDir) + if SnapMountDir == smd { return true } // distros with a non-default /snap location may still be good // if there is a symlink in place that links from the // defaultSnapMountDir (/snap) to the distro specific mount dir - smd := filepath.Join(GlobalRootDir, defaultSnapMountDir) fi, err := os.Lstat(smd) if err == nil && fi.Mode()&os.ModeSymlink != 0 { if target, err := filepath.EvalSymlinks(smd); err == nil { @@ -167,6 +178,8 @@ SnapAppArmorDir = filepath.Join(rootdir, snappyDir, "apparmor", "profiles") AppArmorCacheDir = filepath.Join(rootdir, "/var/cache/apparmor") SnapAppArmorAdditionalDir = filepath.Join(rootdir, snappyDir, "apparmor", "additional") + SnapAppArmorConfineDir = filepath.Join(rootdir, snappyDir, "apparmor", "snap-confine.d") + SnapDownloadCacheDir = filepath.Join(rootdir, snappyDir, "cache") SnapSeccompDir = filepath.Join(rootdir, snappyDir, "seccomp", "bpf") SnapMountPolicyDir = filepath.Join(rootdir, snappyDir, "mount") SnapMetaDir = filepath.Join(rootdir, snappyDir, "meta") @@ -186,6 +199,10 @@ SnapStateFile = filepath.Join(rootdir, snappyDir, "state.json") + SnapCacheDir = filepath.Join(rootdir, "/var/cache/snapd") + SnapNamesFile = filepath.Join(SnapCacheDir, "names") + SnapSectionsFile = filepath.Join(SnapCacheDir, "sections") + SnapSeedDir = filepath.Join(rootdir, snappyDir, "seed") SnapDeviceDir = filepath.Join(rootdir, snappyDir, "device") @@ -193,6 +210,7 @@ SnapRepairStateFile = filepath.Join(SnapRepairDir, "repair.json") SnapRepairRunDir = filepath.Join(SnapRepairDir, "run") SnapRepairAssertsDir = filepath.Join(SnapRepairDir, "assertions") + SnapRunRepairDir = filepath.Join(SnapRunDir, "repair") SnapBinariesDir = filepath.Join(SnapMountDir, "bin") SnapServicesDir = filepath.Join(rootdir, "/etc/systemd/system") @@ -223,4 +241,8 @@ CompletionHelper = filepath.Join(CoreLibExecDir, "etelpmoc.sh") CompletersDir = filepath.Join(rootdir, "/usr/share/bash-completion/completions/") CompleteSh = filepath.Join(SnapMountDir, "core/current/usr/lib/snapd/complete.sh") + + SystemFontsDir = filepath.Join(rootdir, "/usr/share/fonts") + SystemLocalFontsDir = filepath.Join(rootdir, "/usr/local/share/fonts") + SystemFontconfigCacheDir = filepath.Join(rootdir, "/var/cache/fontconfig") } diff -Nru snapd-2.28.5/dirs/dirs_test.go snapd-2.29.3/dirs/dirs_test.go --- snapd-2.28.5/dirs/dirs_test.go 2017-10-10 16:16:09.000000000 +0000 +++ snapd-2.29.3/dirs/dirs_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -58,7 +58,7 @@ dirs.SetRootDir("/") c.Check(dirs.SupportsClassicConfinement(), Equals, true) - dirs.SetRootDir("/alt") + dirs.SnapMountDir = "/alt" defer dirs.SetRootDir("/") c.Check(dirs.SupportsClassicConfinement(), Equals, false) } diff -Nru snapd-2.28.5/errtracker/errtracker.go snapd-2.29.3/errtracker/errtracker.go --- snapd-2.28.5/errtracker/errtracker.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/errtracker/errtracker.go 2017-10-23 06:17:27.000000000 +0000 @@ -83,7 +83,7 @@ } func snapConfineProfileDigest(suffix string) string { - profileText, err := ioutil.ReadFile(snapConfineProfile + suffix) + profileText, err := ioutil.ReadFile(filepath.Join(dirs.GlobalRootDir, snapConfineProfile+suffix)) if err != nil { return "" } @@ -98,10 +98,36 @@ return "no" } +// Report reports an error with the given snap to the error tracker func Report(snap, errMsg, dupSig string, extra map[string]string) (string, error) { + if extra == nil { + extra = make(map[string]string) + } + extra["ProblemType"] = "Snap" + extra["Snap"] = snap + + return report(errMsg, dupSig, extra) +} + +// ReportRepair reports an error with the given repair assertion script +// to the error tracker +func ReportRepair(repair, errMsg, dupSig string, extra map[string]string) (string, error) { + if extra == nil { + extra = make(map[string]string) + } + extra["ProblemType"] = "Repair" + extra["Repair"] = repair + + return report(errMsg, dupSig, extra) +} + +func report(errMsg, dupSig string, extra map[string]string) (string, error) { if CrashDbURLBase == "" { return "", nil } + if extra == nil || extra["ProblemType"] == "" { + return "", fmt.Errorf(`key "ProblemType" not set in %v`, extra) + } machineID, err := readMachineID() if err != nil { @@ -130,14 +156,12 @@ } report := map[string]string{ - "ProblemType": "Snap", "Architecture": arch.UbuntuArchitecture(), "SnapdVersion": SnapdVersion, "DistroRelease": distroRelease(), "HostSnapdBuildID": hostBuildID, "CoreSnapdBuildID": coreBuildID, "Date": timeNow().Format(time.ANSIC), - "Snap": snap, "KernelVersion": release.KernelVersion(), "ErrorMessage": errMsg, "DuplicateSignature": dupSig, diff -Nru snapd-2.28.5/errtracker/errtracker_test.go snapd-2.29.3/errtracker/errtracker_test.go --- snapd-2.28.5/errtracker/errtracker_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/errtracker/errtracker_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -36,6 +36,7 @@ . "gopkg.in/check.v1" "github.com/snapcore/snapd/arch" + "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/errtracker" "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/release" @@ -48,7 +49,11 @@ type ErrtrackerTestSuite struct { testutil.BaseTest - snapConfineProfile string + tmpdir string + + hostBuildID string + coreBuildID string + distroRelease string } var _ = Suite(&ErrtrackerTestSuite{}) @@ -59,8 +64,10 @@ func (s *ErrtrackerTestSuite) SetUpTest(c *C) { s.BaseTest.SetUpTest(c) - d := c.MkDir() - p := filepath.Join(d, "machine-id") + s.tmpdir = c.MkDir() + dirs.SetRootDir(s.tmpdir) + + p := filepath.Join(s.tmpdir, "machine-id") err := ioutil.WriteFile(p, []byte("bbb1a6a5bcdb418380056a2d759c3f7c"), 0644) c.Assert(err, IsNil) s.AddCleanup(errtracker.MockMachineIDPaths([]string{p})) @@ -68,26 +75,32 @@ s.AddCleanup(errtracker.MockCoreSnapd(falsePath)) s.AddCleanup(errtracker.MockReExec(true)) - p = filepath.Join(d, "usr.lib.snapd.snap-confine") - err = ioutil.WriteFile(p, []byte("# fake profile of snap-confine"), 0644) + s.hostBuildID, err = osutil.ReadBuildID(truePath) + c.Assert(err, IsNil) + s.coreBuildID, err = osutil.ReadBuildID(falsePath) c.Assert(err, IsNil) - s.AddCleanup(errtracker.MockSnapConfineApparmorProfile(p)) - s.snapConfineProfile = p + if release.ReleaseInfo.ID == "ubuntu" { + s.distroRelease = fmt.Sprintf("%s %s", strings.Title(release.ReleaseInfo.ID), release.ReleaseInfo.VersionID) + } else { + s.distroRelease = fmt.Sprintf("%s %s", release.ReleaseInfo.ID, release.ReleaseInfo.VersionID) + } } func (s *ErrtrackerTestSuite) TestReport(c *C) { n := 0 identifier := "" - hostBuildID, err := osutil.ReadBuildID(truePath) + + snapConfineProfile := filepath.Join(s.tmpdir, "/etc/apparmor.d/usr.lib.snapd.snap-confine") + err := os.MkdirAll(filepath.Dir(snapConfineProfile), 0755) c.Assert(err, IsNil) - coreBuildID, err := osutil.ReadBuildID(falsePath) + err = ioutil.WriteFile(snapConfineProfile, []byte("# fake profile of snap-confine"), 0644) c.Assert(err, IsNil) - err = ioutil.WriteFile(s.snapConfineProfile+".dpkg-new", []byte{0}, 0644) + err = ioutil.WriteFile(snapConfineProfile+".dpkg-new", []byte{0}, 0644) c.Assert(err, IsNil) - err = ioutil.WriteFile(s.snapConfineProfile+".real", []byte{0}, 0644) + err = ioutil.WriteFile(snapConfineProfile+".real", []byte{0}, 0644) c.Assert(err, IsNil) - err = ioutil.WriteFile(s.snapConfineProfile+".real.dpkg-new", []byte{0}, 0644) + err = ioutil.WriteFile(snapConfineProfile+".real.dpkg-new", []byte{0}, 0644) c.Assert(err, IsNil) prev := errtracker.SnapdVersion @@ -106,32 +119,26 @@ var data map[string]string err = bson.Unmarshal(b, &data) c.Assert(err, IsNil) - var distroRelease string - if release.ReleaseInfo.ID == "ubuntu" { - distroRelease = fmt.Sprintf("%s %s", strings.Title(release.ReleaseInfo.ID), release.ReleaseInfo.VersionID) - } else { - distroRelease = fmt.Sprintf("%s %s", release.ReleaseInfo.ID, release.ReleaseInfo.VersionID) - } c.Check(data, DeepEquals, map[string]string{ - "ProblemType": "Snap", - "DistroRelease": distroRelease, - "HostSnapdBuildID": hostBuildID, - "CoreSnapdBuildID": coreBuildID, + "DistroRelease": s.distroRelease, + "HostSnapdBuildID": s.hostBuildID, + "CoreSnapdBuildID": s.coreBuildID, "SnapdVersion": "some-snapd-version", - "Snap": "some-snap", "Date": "Fri Feb 17 09:51:00 2017", - "Channel": "beta", "KernelVersion": release.KernelVersion(), "ErrorMessage": "failed to do stuff", "DuplicateSignature": "[failed to do stuff]", "Architecture": arch.UbuntuArchitecture(), + "DidSnapdReExec": "yes", + + "ProblemType": "Snap", + "Snap": "some-snap", + "Channel": "beta", "MD5SumSnapConfineAppArmorProfile": "7a7aa5f21063170c1991b84eb8d86de1", "MD5SumSnapConfineAppArmorProfileDpkgNew": "93b885adfe0da089cdf634904fd59f71", "MD5SumSnapConfineAppArmorProfileReal": "93b885adfe0da089cdf634904fd59f71", "MD5SumSnapConfineAppArmorProfileRealDpkgNew": "93b885adfe0da089cdf634904fd59f71", - - "DidSnapdReExec": "yes", }) fmt.Fprintf(w, "c14388aa-f78d-11e6-8df0-fa163eaf9b83 OOPSID") case 1: @@ -222,3 +229,59 @@ c.Check(n, Equals, 1) c.Check(identifiers, DeepEquals, []string{fmt.Sprintf("/%x", sha512.Sum512(machineID))}) } + +func (s *ErrtrackerTestSuite) TestReportRepair(c *C) { + n := 0 + prev := errtracker.SnapdVersion + defer func() { errtracker.SnapdVersion = prev }() + errtracker.SnapdVersion = "some-snapd-version" + + handler := func(w http.ResponseWriter, r *http.Request) { + switch n { + case 0: + c.Check(r.Method, Equals, "POST") + c.Check(r.URL.Path, Matches, "/[a-z0-9]+") + b, err := ioutil.ReadAll(r.Body) + c.Assert(err, IsNil) + + var data map[string]string + err = bson.Unmarshal(b, &data) + c.Assert(err, IsNil) + c.Check(data, DeepEquals, map[string]string{ + "DistroRelease": s.distroRelease, + "HostSnapdBuildID": s.hostBuildID, + "CoreSnapdBuildID": s.coreBuildID, + "SnapdVersion": "some-snapd-version", + "Date": "Fri Feb 17 09:51:00 2017", + "KernelVersion": release.KernelVersion(), + "Architecture": arch.UbuntuArchitecture(), + "DidSnapdReExec": "yes", + + "ProblemType": "Repair", + "Repair": `"repair (1; brand-id:canonical)"`, + "ErrorMessage": "failure in script", + "DuplicateSignature": "[dupSig]", + "BrandID": "canonical", + }) + fmt.Fprintf(w, "c14388aa-f78d-11e6-8df0-fa163eaf9b83 OOPSID") + default: + c.Fatalf("expected one request, got %d", n+1) + } + + n++ + } + + server := httptest.NewServer(http.HandlerFunc(handler)) + defer server.Close() + restorer := errtracker.MockCrashDbURL(server.URL) + defer restorer() + restorer = errtracker.MockTimeNow(func() time.Time { return time.Date(2017, 2, 17, 9, 51, 0, 0, time.UTC) }) + defer restorer() + + id, err := errtracker.ReportRepair(`"repair (1; brand-id:canonical)"`, "failure in script", "[dupSig]", map[string]string{ + "BrandID": "canonical", + }) + c.Check(err, IsNil) + c.Check(id, Equals, "c14388aa-f78d-11e6-8df0-fa163eaf9b83 OOPSID") + c.Check(n, Equals, 1) +} diff -Nru snapd-2.28.5/errtracker/export_test.go snapd-2.29.3/errtracker/export_test.go --- snapd-2.28.5/errtracker/export_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/errtracker/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -66,14 +66,6 @@ } } -func MockSnapConfineApparmorProfile(path string) (restorer func()) { - old := snapConfineProfile - snapConfineProfile = path - return func() { - snapConfineProfile = old - } -} - func MockReExec(didReExec bool) (restorer func()) { old := osutil.GetenvBool("SNAP_DID_REEXEC") if didReExec { diff -Nru snapd-2.28.5/HACKING.md snapd-2.29.3/HACKING.md --- snapd-2.28.5/HACKING.md 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/HACKING.md 2017-10-23 06:17:27.000000000 +0000 @@ -171,6 +171,11 @@ To debug interaction with the snap store, you can set `SNAP_DEBUG_HTTP`. It is a bitfield: dump requests: 1, dump responses: 2, dump bodies: 4. +(make hack: In case you get some security profiles errors when trying to install or refresh a snap, +maybe you need to replace system installed snap-seccomp with the one aligned to the snapd that +you are testing. To do this, simply backup /usr/lib/snapd/snap-seccomp and overwrite it with +the testing one. Don't forget to rollback to the original when finish testing) + # Quick intro to hacking on snap-confine Hey, welcome to the nice, low-level world of snap-confine diff -Nru snapd-2.28.5/httputil/logger_test.go snapd-2.29.3/httputil/logger_test.go --- snapd-2.28.5/httputil/logger_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/httputil/logger_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -39,21 +39,21 @@ func TestHTTPUtil(t *testing.T) { check.TestingT(t) } type loggerSuite struct { - logbuf *bytes.Buffer + logbuf *bytes.Buffer + restoreLogger func() } var _ = check.Suite(&loggerSuite{}) -func (loggerSuite) TearDownTest(c *check.C) { +func (s *loggerSuite) TearDownTest(c *check.C) { os.Unsetenv("SNAPD_DEBUG") + s.restoreLogger() } func (s *loggerSuite) SetUpTest(c *check.C) { os.Setenv("SNAPD_DEBUG", "true") s.logbuf = bytes.NewBuffer(nil) - l, err := logger.New(s.logbuf, logger.DefaultFlags) - c.Assert(err, check.IsNil) - logger.SetLogger(l) + s.logbuf, s.restoreLogger = logger.MockLogger() } func (loggerSuite) TestFlags(c *check.C) { diff -Nru snapd-2.28.5/i18n/dumb/dumb.go snapd-2.29.3/i18n/dumb/dumb.go --- snapd-2.28.5/i18n/dumb/dumb.go 2016-12-08 15:14:07.000000000 +0000 +++ snapd-2.29.3/i18n/dumb/dumb.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -// -*- 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 . - * - */ - -package i18n // import "github.com/snapcore/snapd/i18n/dumb" - -func G(s string) string { - return s -} diff -Nru snapd-2.28.5/image/helpers.go snapd-2.29.3/image/helpers.go --- snapd-2.28.5/image/helpers.go 2017-08-18 13:48:10.000000000 +0000 +++ snapd-2.29.3/image/helpers.go 2017-10-23 06:17:27.000000000 +0000 @@ -25,7 +25,9 @@ "encoding/json" "fmt" "os" + "os/signal" "path/filepath" + "syscall" "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/asserts/snapasserts" @@ -142,11 +144,24 @@ baseName := filepath.Base(snap.MountFile()) targetFn = filepath.Join(targetDir, baseName) - pb := progress.NewTextProgress() + pb := progress.MakeProgressBar() + defer pb.Finished() + + // Intercept sigint + c := make(chan os.Signal, 3) + signal.Notify(c, syscall.SIGINT) + go func() { + <-c + pb.Finished() + os.Exit(1) + }() + if err = sto.Download(context.TODO(), name, targetFn, &snap.DownloadInfo, pb, tsto.user); err != nil { return "", nil, err } + signal.Reset(syscall.SIGINT) + return targetFn, snap, nil } @@ -198,18 +213,9 @@ // Find provides the snapsserts.Finder interface for snapasserts.DerviceSideInfo func (tsto *ToolingStore) Find(at *asserts.AssertionType, headers map[string]string) (asserts.Assertion, error) { - pk := make([]string, len(at.PrimaryKey)) - for i, k := range at.PrimaryKey { - pk[i] = headers[k] - } - as, err := tsto.sto.Assertion(at, pk, tsto.user) + pk, err := asserts.PrimaryKeyFromHeaders(at, headers) if err != nil { - // convert store error to something that the asserts would - // return - if _, ok := err.(*store.AssertionNotFoundError); ok { - return nil, asserts.ErrNotFound - } return nil, err } - return as, nil + return tsto.sto.Assertion(at, pk, tsto.user) } diff -Nru snapd-2.28.5/image/image.go snapd-2.29.3/image/image.go --- snapd-2.28.5/image/image.go 2017-08-18 13:48:10.000000000 +0000 +++ snapd-2.29.3/image/image.go 2017-10-23 06:17:27.000000000 +0000 @@ -105,7 +105,7 @@ local[snapName] = info si, err := snapasserts.DeriveSideInfo(snapName, tsto) - if err != nil && err != asserts.ErrNotFound { + if err != nil && !asserts.IsNotFound(err) { return nil, err } if err == nil { @@ -244,9 +244,6 @@ if osutil.FileExists(cloudConfig) { dst := filepath.Join(cloudDir, "cloud.cfg") err = osutil.CopyFile(cloudConfig, dst, osutil.CopyFlagOverwrite) - } else { - dst := filepath.Join(cloudDir, "cloud-init.disabled") - err = osutil.AtomicWriteFile(dst, nil, 0644, 0) } return err } diff -Nru snapd-2.28.5/image/image_test.go snapd-2.29.3/image/image_test.go --- snapd-2.28.5/image/image_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/image/image_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -58,7 +58,7 @@ } func (s *emptyStore) Assertion(assertType *asserts.AssertionType, primaryKey []string, user *auth.UserState) (asserts.Assertion, error) { - return nil, &store.AssertionNotFoundError{Ref: &asserts.Ref{Type: assertType, PrimaryKey: primaryKey}} + return nil, &asserts.NotFoundError{Type: assertType} } func Test(t *testing.T) { TestingT(t) } @@ -564,9 +564,6 @@ c.Assert(err, IsNil) c.Check(m["snap_core"], Equals, "core_x1.snap") - // check that cloud-init is setup correctly - c.Check(osutil.FileExists(filepath.Join(rootdir, "etc/cloud/cloud-init.disabled")), Equals, true) - c.Check(s.stderr.String(), Equals, "WARNING: \"core\", \"required-snap1\" were installed from local snaps disconnected from a store and cannot be refreshed subsequently!\n") } @@ -675,7 +672,7 @@ dirs.SetRootDir(targetDir) err := image.InstallCloudConfig(emptyGadgetDir) c.Assert(err, IsNil) - c.Check(osutil.FileExists(filepath.Join(targetDir, "etc/cloud/cloud-init.disabled")), Equals, true) + c.Check(osutil.FileExists(filepath.Join(targetDir, "etc/cloud")), Equals, true) } func (s *imageSuite) TestInstallCloudConfigWithCloudConfig(c *C) { @@ -827,8 +824,5 @@ c.Assert(err, IsNil) c.Check(m["snap_core"], Equals, "core_3.snap") - // check that cloud-init is setup correctly - c.Check(osutil.FileExists(filepath.Join(rootdir, "etc/cloud/cloud-init.disabled")), Equals, true) - c.Check(s.stderr.String(), Equals, "") } diff -Nru snapd-2.28.5/interfaces/apparmor/backend.go snapd-2.29.3/interfaces/apparmor/backend.go --- snapd-2.28.5/interfaces/apparmor/backend.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/interfaces/apparmor/backend.go 2017-10-27 12:31:06.000000000 +0000 @@ -118,6 +118,13 @@ return err } + // create for policy extensions for snap-confine. This is required for the + // profiles to compile but distribution package may not yet contain this + // directory. + if err := os.MkdirAll(dirs.SnapAppArmorConfineDir, 0755); err != nil { + return err + } + // not using apparmor.LoadProfile() because it uses a different cachedir if output, err := exec.Command("apparmor_parser", "--replace", "--write-cache", apparmorProfilePath, "--cache-loc", dirs.SystemApparmorCacheDir).CombinedOutput(); err != nil { return fmt.Errorf("cannot replace snap-confine apparmor profile: %v", osutil.OutputErr(output, err)) @@ -145,6 +152,21 @@ logger.Noticef("cannot create host snap-confine apparmor configuration: %s", err) } } + // core on core devices is also special, the apparmor cache gets + // confused too easy, especially at rollbacks, so we delete the cache. + // See LP:#1460152 and + // https://forum.snapcraft.io/t/core-snap-revert-issues-on-core-devices/ + if snapName == "core" && !release.OnClassic { + if li, err := filepath.Glob(filepath.Join(dirs.SystemApparmorCacheDir, "*")); err == nil { + for _, p := range li { + if st, err := os.Stat(p); err == nil && st.Mode().IsRegular() { + if err := os.Remove(p); err != nil { + logger.Noticef("cannot remove %q: %s", p, err) + } + } + } + } + } // Get the files that this snap should have content, err := b.deriveContent(spec.(*Specification), snapInfo, opts) @@ -216,7 +238,11 @@ func addContent(securityTag string, snapInfo *snap.Info, opts interfaces.ConfinementOptions, snippetForTag string, content map[string]*osutil.FileState) { var policy string - if opts.Classic && !opts.JailMode { + // When partial AppArmor is detected, use the classic template for now. We could + // use devmode, but that could generate confusing log entries for users running + // snaps on systems with partial AppArmor support. + level := release.AppArmorLevel() + if level == release.PartialAppArmor || (opts.Classic && !opts.JailMode) { policy = classicTemplate } else { policy = defaultTemplate @@ -232,13 +258,12 @@ return fmt.Sprintf("profile \"%s\"", securityTag) case "###SNIPPETS###": var tagSnippets string - if opts.Classic && opts.JailMode { // Add a special internal snippet for snaps using classic confinement // and jailmode together. This snippet provides access to the core snap // so that the dynamic linker and shared libraries can be used. tagSnippets = classicJailmodeSnippet + "\n" + snippetForTag - } else if opts.Classic && !opts.JailMode { + } else if level == release.PartialAppArmor || (opts.Classic && !opts.JailMode) { // When classic confinement (without jailmode) is in effect we // are ignoring all apparmor snippets as they may conflict with // the super-broad template we are starting with. diff -Nru snapd-2.28.5/interfaces/apparmor/backend_test.go snapd-2.29.3/interfaces/apparmor/backend_test.go --- snapd-2.28.5/interfaces/apparmor/backend_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/apparmor/backend_test.go 2017-10-27 12:31:06.000000000 +0000 @@ -293,6 +293,9 @@ } func (s *backendSuite) TestRealDefaultTemplateIsNormallyUsed(c *C) { + restore := release.MockAppArmorLevel(release.FullAppArmor) + defer restore() + snapInfo := snaptest.MockInfo(c, ifacetest.SambaYamlV1, nil) // NOTE: we don't call apparmor.MockTemplate() err := s.Backend.Setup(snapInfo, interfaces.ConfinementOptions{}, s.Repo) @@ -366,6 +369,9 @@ }} func (s *backendSuite) TestCombineSnippets(c *C) { + restore := release.MockAppArmorLevel(release.FullAppArmor) + defer restore() + // NOTE: replace the real template with a shorter variant restoreTemplate := apparmor.MockTemplate("\n" + "###VAR###\n" + @@ -485,4 +491,36 @@ {"apparmor_parser", "--replace", "--write-cache", newAA[0], "--cache-loc", dirs.SystemApparmorCacheDir}, }) + // snap-confine.d was created + _, err = os.Stat(dirs.SnapAppArmorConfineDir) + c.Check(err, IsNil) + +} + +func (s *backendSuite) TestCoreOnCoreCleansApparmorCache(c *C) { + restorer := release.MockOnClassic(false) + defer restorer() + + err := os.MkdirAll(dirs.SystemApparmorCacheDir, 0755) + c.Assert(err, IsNil) + // the canary file in the cache will be removed + canaryPath := filepath.Join(dirs.SystemApparmorCacheDir, "meep") + err = ioutil.WriteFile(canaryPath, nil, 0644) + c.Assert(err, IsNil) + // but non-regular entries in the cache dir are kept + dirsAreKept := filepath.Join(dirs.SystemApparmorCacheDir, "dir") + err = os.MkdirAll(dirsAreKept, 0755) + c.Assert(err, IsNil) + symlinksAreKept := filepath.Join(dirs.SystemApparmorCacheDir, "symlink") + err = os.Symlink("some-sylink-target", symlinksAreKept) + c.Assert(err, IsNil) + + // install the new core snap on classic triggers a new snap-confine + // for this snap-confine on core + s.InstallSnap(c, interfaces.ConfinementOptions{}, coreYaml, 111) + + l, err := filepath.Glob(filepath.Join(dirs.SystemApparmorCacheDir, "*")) + c.Assert(err, IsNil) + // canary is gone, extra stuff is kept + c.Check(l, DeepEquals, []string{dirsAreKept, symlinksAreKept}) } diff -Nru snapd-2.28.5/interfaces/apparmor/template.go snapd-2.29.3/interfaces/apparmor/template.go --- snapd-2.28.5/interfaces/apparmor/template.go 2017-09-15 15:05:07.000000000 +0000 +++ snapd-2.29.3/interfaces/apparmor/template.go 2017-10-30 13:27:31.000000000 +0000 @@ -43,6 +43,12 @@ # for series 16 and cross-distro /etc/ld.so.preload r, + # The base abstraction doesn't yet have this + /lib/terminfo/** rk, + /usr/share/terminfo/** k, + /usr/share/zoneinfo/** k, + owner @{PROC}/@{pid}/maps k, + # for python apps/services #include /usr/bin/python{,2,2.[0-9]*,3,3.[0-9]*} ixr, @@ -226,8 +232,10 @@ /usr/share/distro-info/*.csv r, # Allow reading /etc/os-release. On Ubuntu 16.04+ it is a symlink to /usr/lib - # but on 14.04 it is an actual file so it doens't fall under other rules. - /etc/os-release r, + # which is allowed by the base abstraction, but on 14.04 it is an actual file + # so need to add it here. Also allow read locks on the file. + /etc/os-release rk, + /usr/lib/os-release k, # systemd native journal API (see sd_journal_print(4)). This should be in # AppArmor's base abstraction, but until it is, include here. @@ -278,6 +286,7 @@ /etc/{,writable/}localtime r, /etc/{,writable/}mailname r, /etc/{,writable/}timezone r, + owner @{PROC}/@{pid}/cgroup r, @{PROC}/@{pid}/io r, owner @{PROC}/@{pid}/limits r, owner @{PROC}/@{pid}/loginuid r, @@ -292,6 +301,7 @@ @{PROC}/@{pid}/task/[0-9]*/status r, @{PROC}/sys/kernel/hostname r, @{PROC}/sys/kernel/osrelease r, + @{PROC}/sys/kernel/ostype r, @{PROC}/sys/kernel/yama/ptrace_scope r, @{PROC}/sys/kernel/shmmax r, @{PROC}/sys/fs/file-max r, @@ -299,6 +309,8 @@ @{PROC}/sys/kernel/random/uuid r, @{PROC}/sys/kernel/random/boot_id r, /sys/devices/virtual/tty/{console,tty*}/active r, + /sys/fs/cgroup/memory/memory.limit_in_bytes r, + /sys/fs/cgroup/memory/snap.@{SNAP_NAME}{,.*}/memory.limit_in_bytes r, /{,usr/}lib/ r, # Reads of oom_adj and oom_score_adj are safe @@ -409,6 +421,19 @@ capability sys_chroot, /{,usr/}sbin/chroot ixr, + # Lttng tracing is very noisy and should not be allowed by confined apps. Can + # safely deny for the normal case (LP: #1260491). If/when an lttng-trace + # interface is needed, we can rework this. + deny /{dev,run,var/run}/shm/lttng-ust-* rw, + + # Allow read-access on /home/ for navigating to other parts of the + # filesystem. While this allows enumerating users, this is already allowed + # via /etc/passwd and getent. + @{HOMEDIRS}/ r, + + # Allow read-access to / for navigating to other parts of the filesystem. + / r, + ###SNIPPETS### } ` diff -Nru snapd-2.28.5/interfaces/backends/backends.go snapd-2.29.3/interfaces/backends/backends.go --- snapd-2.28.5/interfaces/backends/backends.go 2016-10-27 13:22:15.000000000 +0000 +++ snapd-2.29.3/interfaces/backends/backends.go 2017-10-23 06:17:27.000000000 +0000 @@ -1,7 +1,7 @@ // -*- Mode: Go; indent-tabs-mode: t -*- /* - * Copyright (C) 2016 Canonical Ltd + * Copyright (C) 2016-2017 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 @@ -20,6 +20,8 @@ package backends import ( + "fmt" + "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/apparmor" "github.com/snapcore/snapd/interfaces/dbus" @@ -31,18 +33,40 @@ "github.com/snapcore/snapd/release" ) -// append when a new security backend is added -var All = []interfaces.SecurityBackend{ - &systemd.Backend{}, - &seccomp.Backend{}, - &dbus.Backend{}, - &udev.Backend{}, - &mount.Backend{}, - &kmod.Backend{}, -} +var All []interfaces.SecurityBackend = backends() + +func backends() []interfaces.SecurityBackend { + all := []interfaces.SecurityBackend{ + &systemd.Backend{}, + &seccomp.Backend{}, + &dbus.Backend{}, + &udev.Backend{}, + &mount.Backend{}, + &kmod.Backend{}, + } + + // This should be logger.Noticef but due to ordering of initialization + // calls, the logger is not ready at this point yet and the message goes + // nowhere. Per advice from other snapd developers, we just print it + // directly. + // + // TODO: on this should become a user-visible message via the user-warning + // framework, so that users are aware that we have non-strict confinement. + // By printing this directly we ensure it will end up the journal for the + // snapd.service. This aspect should be retained even after the switch to + // user-warning. + fmt.Printf("AppArmor status: %s\n", release.AppArmorSummary()) -func init() { - if !release.ReleaseInfo.ForceDevMode() { - All = append(All, &apparmor.Backend{}) + // Enable apparmor backend if there is any level of apparmor support, + // including partial feature set. This will allow snap-confine to always + // link to apparmor and check if it is enabled on boot, knowing that there + // is always *some* profile to apply to each snap process. + // + // When some features are missing the backend will generate more permissive + // profiles that keep applications operational, in forced-devmode. + switch release.AppArmorLevel() { + case release.FullAppArmor, release.PartialAppArmor: + all = append(all, &apparmor.Backend{}) } + return all } diff -Nru snapd-2.28.5/interfaces/backends/backends_test.go snapd-2.29.3/interfaces/backends/backends_test.go --- snapd-2.28.5/interfaces/backends/backends_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/interfaces/backends/backends_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 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 . + * + */ + +package backends_test + +import ( + "testing" + + . "gopkg.in/check.v1" + + "github.com/snapcore/snapd/interfaces/backends" + "github.com/snapcore/snapd/release" + "github.com/snapcore/snapd/testutil" +) + +func Test(t *testing.T) { + TestingT(t) +} + +type backendsSuite struct{} + +var _ = Suite(&backendsSuite{}) + +func (s *backendsSuite) TestIsAppArmorEnabled(c *C) { + for _, level := range []release.AppArmorLevelType{release.NoAppArmor, release.PartialAppArmor, release.FullAppArmor} { + restore := release.MockAppArmorLevel(level) + defer restore() + + all := backends.Backends() + names := make([]string, len(all)) + for i, backend := range all { + names[i] = string(backend.Name()) + } + + if level == release.NoAppArmor { + c.Assert(names, Not(testutil.Contains), "apparmor") + } else { + c.Assert(names, testutil.Contains, "apparmor") + } + } +} diff -Nru snapd-2.28.5/interfaces/backends/export_test.go snapd-2.29.3/interfaces/backends/export_test.go --- snapd-2.28.5/interfaces/backends/export_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/interfaces/backends/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,24 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package backends + +var ( + Backends = backends +) diff -Nru snapd-2.28.5/interfaces/builtin/all.go snapd-2.29.3/interfaces/builtin/all.go --- snapd-2.28.5/interfaces/builtin/all.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/all.go 2017-10-23 06:17:27.000000000 +0000 @@ -24,8 +24,13 @@ "sort" "github.com/snapcore/snapd/interfaces" + "github.com/snapcore/snapd/snap" ) +func init() { + snap.SanitizePlugsSlots = SanitizePlugsSlots +} + var ( allInterfaces map[string]interfaces.Interface ) @@ -51,6 +56,44 @@ allInterfaces[iface.Name()] = iface } +func SanitizePlugsSlots(snapInfo *snap.Info) { + for plugName, plugInfo := range snapInfo.Plugs { + iface, ok := allInterfaces[plugInfo.Interface] + if !ok { + snapInfo.BadInterfaces[plugName] = fmt.Sprintf("unknown interface %q", plugInfo.Interface) + continue + } + // Reject plug with invalid name + if err := interfaces.ValidateName(plugName); err != nil { + snapInfo.BadInterfaces[plugName] = err.Error() + continue + } + plug := &interfaces.Plug{PlugInfo: plugInfo} + if err := plug.Sanitize(iface); err != nil { + snapInfo.BadInterfaces[plugName] = err.Error() + continue + } + } + + for slotName, slotInfo := range snapInfo.Slots { + iface, ok := allInterfaces[slotInfo.Interface] + if !ok { + snapInfo.BadInterfaces[slotName] = fmt.Sprintf("unknown interface %q", slotInfo.Interface) + continue + } + // Reject slot with invalid name + if err := interfaces.ValidateName(slotName); err != nil { + snapInfo.BadInterfaces[slotName] = err.Error() + continue + } + slot := &interfaces.Slot{SlotInfo: slotInfo} + if err := slot.Sanitize(iface); err != nil { + snapInfo.BadInterfaces[slotName] = err.Error() + continue + } + } +} + type byIfaceName []interfaces.Interface func (c byIfaceName) Len() int { return len(c) } diff -Nru snapd-2.28.5/interfaces/builtin/all_test.go snapd-2.29.3/interfaces/builtin/all_test.go --- snapd-2.28.5/interfaces/builtin/all_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/all_test.go 2017-11-03 05:49:30.000000000 +0000 @@ -20,7 +20,9 @@ package builtin_test import ( + "fmt" "reflect" + "strings" "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/apparmor" @@ -32,6 +34,8 @@ "github.com/snapcore/snapd/interfaces/seccomp" "github.com/snapcore/snapd/interfaces/systemd" "github.com/snapcore/snapd/interfaces/udev" + "github.com/snapcore/snapd/snap" + "github.com/snapcore/snapd/snap/snaptest" . "gopkg.in/check.v1" ) @@ -307,3 +311,117 @@ // Duplicates are detected. c.Assert(func() { builtin.RegisterIface(iface) }, PanicMatches, `cannot register duplicate interface "foo"`) } + +const testConsumerInvalidSlotNameYaml = ` +name: consumer +slots: + ttyS5: + interface: iface +apps: + app: + slots: [iface] +` + +const testConsumerInvalidPlugNameYaml = ` +name: consumer +plugs: + ttyS3: + interface: iface +apps: + app: + plugs: [iface] +` + +func (s *AllSuite) TestSanitizeErrorsOnInvalidSlotNames(c *C) { + restore := builtin.MockInterfaces(map[string]interfaces.Interface{ + "iface": &ifacetest.TestInterface{InterfaceName: "iface"}, + }) + defer restore() + + snapInfo := snaptest.MockInfo(c, testConsumerInvalidSlotNameYaml, nil) + snap.SanitizePlugsSlots(snapInfo) + c.Assert(snapInfo.BadInterfaces, HasLen, 1) + c.Check(snap.BadInterfacesSummary(snapInfo), Matches, `snap "consumer" has bad plugs or slots: ttyS5 \(invalid interface name: "ttyS5"\)`) +} + +func (s *AllSuite) TestSanitizeErrorsOnInvalidPlugNames(c *C) { + restore := builtin.MockInterfaces(map[string]interfaces.Interface{ + "iface": &ifacetest.TestInterface{InterfaceName: "iface"}, + }) + defer restore() + + snapInfo := snaptest.MockInfo(c, testConsumerInvalidPlugNameYaml, nil) + snap.SanitizePlugsSlots(snapInfo) + c.Assert(snapInfo.BadInterfaces, HasLen, 1) + c.Check(snap.BadInterfacesSummary(snapInfo), Matches, `snap "consumer" has bad plugs or slots: ttyS3 \(invalid interface name: "ttyS3"\)`) +} + +func (s *AllSuite) TestUnexpectedSpecSignatures(c *C) { + type funcSig struct { + name string + in []string + out []string + } + var sigs []funcSig + + // All the valid signatures from all the specification definers from all the backends. + for _, backend := range []string{"AppArmor", "SecComp", "UDev", "DBus", "Systemd", "KMod"} { + backendLower := strings.ToLower(backend) + sigs = append(sigs, []funcSig{{ + name: fmt.Sprintf("%sPermanentPlug", backend), + in: []string{ + fmt.Sprintf("*%s.Specification", backendLower), + "*interfaces.Plug", + }, + out: []string{"error"}, + }, { + name: fmt.Sprintf("%sPermanentSlot", backend), + in: []string{ + fmt.Sprintf("*%s.Specification", backendLower), + "*interfaces.Slot", + }, + out: []string{"error"}, + }, { + name: fmt.Sprintf("%sConnectedPlug", backend), + in: []string{ + fmt.Sprintf("*%s.Specification", backendLower), + "*interfaces.Plug", + "map[string]interface {}", + "*interfaces.Slot", + "map[string]interface {}", + }, + out: []string{"error"}, + }, { + name: fmt.Sprintf("%sConnectedSlot", backend), + in: []string{ + fmt.Sprintf("*%s.Specification", backendLower), + "*interfaces.Plug", + "map[string]interface {}", + "*interfaces.Slot", + "map[string]interface {}", + }, + out: []string{"error"}, + }}...) + } + for _, iface := range builtin.Interfaces() { + ifaceVal := reflect.ValueOf(iface) + ifaceType := ifaceVal.Type() + for _, sig := range sigs { + meth, ok := ifaceType.MethodByName(sig.name) + if !ok { + // all specificiation methods are optional. + continue + } + methType := meth.Type + // Check that the signature matches our expectation. The -1 and +1 below is for the receiver type. + c.Assert(methType.NumIn()-1, Equals, len(sig.in), Commentf("expected %s's %s method to take %d arguments", ifaceType, meth.Name, len(sig.in))) + for i, expected := range sig.in { + c.Assert(methType.In(i+1).String(), Equals, expected, Commentf("expected %s's %s method %dth argument type to be different", ifaceType, meth.Name, i)) + } + c.Assert(methType.NumOut(), Equals, len(sig.out), Commentf("expected %s's %s method to return %d values", ifaceType, meth.Name, len(sig.out))) + for i, expected := range sig.out { + c.Assert(methType.Out(i).String(), Equals, expected, Commentf("expected %s's %s method %dth return value type to be different", ifaceType, meth.Name, i)) + } + } + } +} diff -Nru snapd-2.28.5/interfaces/builtin/alsa.go snapd-2.29.3/interfaces/builtin/alsa.go --- snapd-2.28.5/interfaces/builtin/alsa.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/alsa.go 2017-11-09 12:08:52.000000000 +0000 @@ -46,15 +46,15 @@ @{PROC}/asound/** rw, ` -const alsaConnectedPlugUDev = ` -KERNEL=="controlC[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="hwC[0-9]*D[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="pcmC[0-9]*D[0-9]*[cp]", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="midiC[0-9]*D[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="timer", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="seq", TAG+="###CONNECTED_SECURITY_TAGS###" -SUBSYSTEM=="sound", KERNEL=="card[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###" -` +var alsaConnectedPlugUDev = []string{ + `KERNEL=="controlC[0-9]*"`, + `KERNEL=="hwC[0-9]*D[0-9]*"`, + `KERNEL=="pcmC[0-9]*D[0-9]*[cp]"`, + `KERNEL=="midiC[0-9]*D[0-9]*"`, + `KERNEL=="timer"`, + `KERNEL=="seq"`, + `SUBSYSTEM=="sound", KERNEL=="card[0-9]*"`, +} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/alsa_test.go snapd-2.29.3/interfaces/builtin/alsa_test.go --- snapd-2.28.5/interfaces/builtin/alsa_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/alsa_test.go 2017-11-09 12:08:52.000000000 +0000 @@ -86,8 +86,8 @@ func (s *AlsaInterfaceSuite) TestUDevpec(c *C) { spec := &udev.Specification{} c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.slot, nil), IsNil) - c.Assert(spec.Snippets(), HasLen, 1) - c.Assert(spec.Snippets()[0], testutil.Contains, `KERNEL=="pcmC[0-9]*D[0-9]*[cp]", TAG+="snap_consumer_app"`) + c.Assert(spec.Snippets(), HasLen, 7) + c.Assert(spec.Snippets(), testutil.Contains, `KERNEL=="pcmC[0-9]*D[0-9]*[cp]", TAG+="snap_consumer_app"`) } func (s *AlsaInterfaceSuite) TestStaticInfo(c *C) { diff -Nru snapd-2.28.5/interfaces/builtin/bluetooth_control.go snapd-2.29.3/interfaces/builtin/bluetooth_control.go --- snapd-2.28.5/interfaces/builtin/bluetooth_control.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/bluetooth_control.go 2017-11-09 12:08:52.000000000 +0000 @@ -56,7 +56,7 @@ bind ` -const bluetoothControlConnectedPlugUDev = `SUBSYSTEM=="bluetooth", TAG+="###CONNECTED_SECURITY_TAGS###"` +var bluetoothControlConnectedPlugUDev = []string{`SUBSYSTEM=="bluetooth"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/bluez.go snapd-2.29.3/interfaces/builtin/bluez.go --- snapd-2.28.5/interfaces/builtin/bluez.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/bluez.go 2017-11-09 12:08:52.000000000 +0000 @@ -91,11 +91,12 @@ bus=system name="org.bluez.obex", - # Allow traffic to/from our path and interface with any method for unconfined - # cliens to talk to our bluez services. + # Allow traffic to/from our interface with any method for unconfined clients + # to talk to our bluez services. For the org.bluez interface we don't specify + # an Object Path since according to the bluez specification these can be + # anything (https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc). dbus (receive, send) bus=system - path=/org/bluez{,/**} interface=org.bluez.* peer=(label=unconfined), dbus (receive, send) @@ -196,8 +197,6 @@ ` -const bluezConnectedPlugUDev = `KERNEL=="rfkill", TAG+="###CONNECTED_SECURITY_TAGS###"` - type bluezInterface struct{} func (iface *bluezInterface) Name() string { @@ -243,12 +242,7 @@ } func (iface *bluezInterface) UDevConnectedPlug(spec *udev.Specification, plug *interfaces.Plug, plugAttrs map[string]interface{}, slot *interfaces.Slot, slotAttrs map[string]interface{}) error { - old := "###CONNECTED_SECURITY_TAGS###" - for appName := range plug.Apps { - tag := udevSnapSecurityName(plug.Snap.Name(), appName) - snippet := strings.Replace(bluezConnectedPlugUDev, old, tag, -1) - spec.AddSnippet(snippet) - } + spec.TagDevice(`KERNEL=="rfkill"`) return nil } diff -Nru snapd-2.28.5/interfaces/builtin/broadcom_asic_control.go snapd-2.29.3/interfaces/builtin/broadcom_asic_control.go --- snapd-2.28.5/interfaces/builtin/broadcom_asic_control.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/broadcom_asic_control.go 2017-11-09 12:08:52.000000000 +0000 @@ -49,10 +49,10 @@ /run/udev/data/+pci:[0-9]* r, ` -const broadcomAsicControlConnectedPlugUDev = ` -SUBSYSTEM=="pci", DRIVER=="linux-kernel-bde", TAG+="###CONNECTED_SECURITY_TAGS###" -SUBSYSTEM=="net", KERNEL=="bcm[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###" -` +var broadcomAsicControlConnectedPlugUDev = []string{ + `SUBSYSTEM=="pci", DRIVER=="linux-kernel-bde"`, + `SUBSYSTEM=="net", KERNEL=="bcm[0-9]*"`, +} // The upstream linux kernel doesn't come with support for the // necessary kernel modules we need to drive a Broadcom ASIC. diff -Nru snapd-2.28.5/interfaces/builtin/broadcom_asic_control_test.go snapd-2.29.3/interfaces/builtin/broadcom_asic_control_test.go --- snapd-2.28.5/interfaces/builtin/broadcom_asic_control_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/broadcom_asic_control_test.go 2017-11-09 12:08:52.000000000 +0000 @@ -89,8 +89,8 @@ func (s *BroadcomAsicControlSuite) TestUDevSpec(c *C) { spec := &udev.Specification{} c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.slot, nil), IsNil) - c.Assert(spec.Snippets(), HasLen, 1) - c.Assert(spec.Snippets()[0], testutil.Contains, `SUBSYSTEM=="net", KERNEL=="bcm[0-9]*", TAG+="snap_consumer_app"`) + c.Assert(spec.Snippets(), HasLen, 2) + c.Assert(spec.Snippets(), testutil.Contains, `SUBSYSTEM=="net", KERNEL=="bcm[0-9]*", TAG+="snap_consumer_app"`) } func (s *BroadcomAsicControlSuite) TestKModSpec(c *C) { diff -Nru snapd-2.28.5/interfaces/builtin/browser_support.go snapd-2.29.3/interfaces/builtin/browser_support.go --- snapd-2.28.5/interfaces/builtin/browser_support.go 2017-09-15 15:05:07.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/browser_support.go 2017-11-09 07:19:00.000000000 +0000 @@ -55,9 +55,15 @@ # Chrome/Chromium should be modified to use snap.$SNAP_NAME.* or the snap # packaging adjusted to use LD_PRELOAD technique from LP: #1577514 -owner /{dev,run}/shm/{,.}org.chromium.Chromium.* mrw, +owner /{dev,run}/shm/{,.}org.chromium.* mrw, owner /{dev,run}/shm/{,.}com.google.Chrome.* mrw, +# Chrome's Singleton API sometimes causes an ouid/fsuid mismatch denial, so +# for now, allow non-owner read on the singleton socket (LP: #1731012). See +# https://forum.snapcraft.io/t/electron-snap-killed-when-using-app-makesingleinstance-api/2667/20 +/run/user/[0-9]*/snap.@{SNAP_NAME}/{,.}org.chromium.*/SS r, +/run/user/[0-9]*/snap.@{SNAP_NAME}/{,.}com.google.Chrome.*/SS r, + # Allow reading platform files /run/udev/data/+platform:* r, @@ -66,6 +72,7 @@ # Chromium content api in gnome-shell reads this /etc/opt/chrome/{,**} r, +/etc/chromium/{,**} r, # Chrome/Chromium should be adjusted to not use gconf. It is only used with # legacy systems that don't have snapd @@ -73,13 +80,15 @@ bus=session interface="org.gnome.GConf.Server", -# Lttng tracing is very noisy and should not be allowed by confined apps. Can -# safely deny. LP: #1260491 -deny /{dev,run,var/run}/shm/lttng-ust-* rw, - # webbrowser-app/webapp-container tries to read this file to determine if it is # confined or not, so explicitly deny to avoid noise in the logs. deny @{PROC}/@{pid}/attr/current r, + +# This is an information leak but disallowing it leads to developer confusion +# when using the chromium content api file chooser due to a (harmless) glib +# warning and the noisy AppArmor denial. +owner @{PROC}/@{pid}/mounts r, +owner @{PROC}/@{pid}/mountinfo r, ` const browserSupportConnectedPlugAppArmorWithoutSandbox = ` @@ -131,6 +140,7 @@ /run/udev/data/c89:[0-9]* r, # /dev/i2c-* /run/udev/data/c81:[0-9]* r, # video4linux (/dev/video*, etc) /run/udev/data/c202:[0-9]* r, # /dev/cpu/*/msr +/run/udev/data/c203:[0-9]* r, # /dev/cuse /run/udev/data/+acpi:* r, /run/udev/data/+hwmon:hwmon[0-9]* r, /run/udev/data/+i2c:* r, @@ -153,6 +163,7 @@ /run/udev/data/n[0-9]* r, /run/udev/data/+bluetooth:hci[0-9]* r, /run/udev/data/+rfkill:rfkill[0-9]* r, +/run/udev/data/c241:[0-9]* r, # /dev/vhost-vsock # storage /run/udev/data/b1:[0-9]* r, # /dev/ram* diff -Nru snapd-2.28.5/interfaces/builtin/camera.go snapd-2.29.3/interfaces/builtin/camera.go --- snapd-2.28.5/interfaces/builtin/camera.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/camera.go 2017-11-09 12:08:52.000000000 +0000 @@ -42,7 +42,7 @@ /sys/devices/pci**/usb*/**/video4linux/** r, ` -const cameraConnectedPlugUDev = `KERNEL=="video[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###"` +var cameraConnectedPlugUDev = []string{`KERNEL=="video[0-9]*"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/common.go snapd-2.29.3/interfaces/builtin/common.go --- snapd-2.28.5/interfaces/builtin/common.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/common.go 2017-11-09 12:08:52.000000000 +0000 @@ -21,7 +21,6 @@ import ( "path/filepath" - "strings" "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/apparmor" @@ -49,7 +48,7 @@ connectedPlugAppArmor string connectedPlugSecComp string - connectedPlugUDev string + connectedPlugUDev []string reservedForOS bool rejectAutoConnectPairs bool @@ -147,13 +146,8 @@ } func (iface *commonInterface) UDevConnectedPlug(spec *udev.Specification, plug *interfaces.Plug, plugAttrs map[string]interface{}, slot *interfaces.Slot, slotAttrs map[string]interface{}) error { - old := "###CONNECTED_SECURITY_TAGS###" - if iface.connectedPlugUDev != "" { - for appName := range plug.Apps { - tag := udevSnapSecurityName(plug.Snap.Name(), appName) - snippet := strings.Replace(iface.connectedPlugUDev, old, tag, -1) - spec.AddSnippet(snippet) - } + for _, rule := range iface.connectedPlugUDev { + spec.TagDevice(rule) } return nil } diff -Nru snapd-2.28.5/interfaces/builtin/common_test.go snapd-2.29.3/interfaces/builtin/common_test.go --- snapd-2.28.5/interfaces/builtin/common_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/common_test.go 2017-11-09 12:08:52.000000000 +0000 @@ -49,14 +49,14 @@ // common interface can define connected plug udev rules iface := &commonInterface{ name: "common", - connectedPlugUDev: `KERNEL="foo", TAG+="###CONNECTED_SECURITY_TAGS###"`, + connectedPlugUDev: []string{`KERNEL=="foo"`}, } spec := &udev.Specification{} c.Assert(spec.AddConnectedPlug(iface, plug, nil, slot, nil), IsNil) c.Assert(spec.Snippets(), DeepEquals, []string{ - `KERNEL="foo", TAG+="snap_consumer_app-a"`, + `KERNEL=="foo", TAG+="snap_consumer_app-a"`, // NOTE: app-b is unaffected as it doesn't have a plug reference. - `KERNEL="foo", TAG+="snap_consumer_app-c"`, + `KERNEL=="foo", TAG+="snap_consumer_app-c"`, }) // connected plug udev rules are optional diff -Nru snapd-2.28.5/interfaces/builtin/cups_control.go snapd-2.29.3/interfaces/builtin/cups_control.go --- snapd-2.28.5/interfaces/builtin/cups_control.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/cups_control.go 2017-11-09 07:19:00.000000000 +0000 @@ -34,6 +34,7 @@ # privileged access to configure printing. #include +/run/cups/printcap r, ` func init() { diff -Nru snapd-2.28.5/interfaces/builtin/desktop.go snapd-2.29.3/interfaces/builtin/desktop.go --- snapd-2.28.5/interfaces/builtin/desktop.go 2017-09-15 15:05:07.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/desktop.go 2017-10-30 13:27:31.000000000 +0000 @@ -19,6 +19,15 @@ package builtin +import ( + "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/interfaces" + "github.com/snapcore/snapd/interfaces/apparmor" + "github.com/snapcore/snapd/interfaces/mount" + "github.com/snapcore/snapd/osutil" + "github.com/snapcore/snapd/release" +) + const desktopSummary = `allows access to basic graphical desktop resources` const desktopBaseDeclarationSlots = ` @@ -62,7 +71,10 @@ # subset of freedesktop.org owner @{HOME}/.local/share/mime/** r, -owner @{HOME}/.config/user-dirs.dirs r, +owner @{HOME}/.config/user-dirs.* r, + +/etc/xdg/user-dirs.conf r, +/etc/xdg/user-dirs.defaults r, # gmenu dbus (send) @@ -119,19 +131,61 @@ interface=io.snapcraft.Launcher member=OpenURL peer=(label=unconfined), - -# Lttng tracing is very noisy and should not be allowed by confined apps. Can -# safely deny. LP: #1260491 -deny /{dev,run,var/run}/shm/lttng-ust-* rw, ` +type desktopInterface struct{} + +func (iface *desktopInterface) Name() string { + return "desktop" +} + +func (iface *desktopInterface) StaticInfo() interfaces.StaticInfo { + return interfaces.StaticInfo{ + Summary: desktopSummary, + ImplicitOnClassic: true, + BaseDeclarationSlots: desktopBaseDeclarationSlots, + } +} + +func (iface *desktopInterface) SanitizeSlot(slot *interfaces.Slot) error { + return sanitizeSlotReservedForOS(iface, slot) +} + +func (iface *desktopInterface) AutoConnect(*interfaces.Plug, *interfaces.Slot) bool { + // allow what declarations allowed + return true +} + +func (iface *desktopInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.Plug, plugAttrs map[string]interface{}, slot *interfaces.Slot, slotAttrs map[string]interface{}) error { + spec.AddSnippet(desktopConnectedPlugAppArmor) + return nil +} + +func (iface *desktopInterface) MountConnectedPlug(spec *mount.Specification, plug *interfaces.Plug, plugAttrs map[string]interface{}, slot *interfaces.Slot, slotAttrs map[string]interface{}) error { + if !release.OnClassic { + // There is nothing to expose on an all-snaps system + return nil + } + + fontconfigDirs := []string{ + dirs.SystemFontsDir, + dirs.SystemLocalFontsDir, + dirs.SystemFontconfigCacheDir, + } + + for _, dir := range fontconfigDirs { + if !osutil.IsDirectory(dir) { + continue + } + spec.AddMountEntry(mount.Entry{ + Name: "/var/lib/snapd/hostfs" + dir, + Dir: dirs.StripRootDir(dir), + Options: []string{"bind", "ro"}, + }) + } + return nil +} + func init() { - registerIface(&commonInterface{ - name: "desktop", - summary: desktopSummary, - implicitOnClassic: true, - baseDeclarationSlots: desktopBaseDeclarationSlots, - connectedPlugAppArmor: desktopConnectedPlugAppArmor, - reservedForOS: true, - }) + registerIface(&desktopInterface{}) } diff -Nru snapd-2.28.5/interfaces/builtin/desktop_test.go snapd-2.29.3/interfaces/builtin/desktop_test.go --- snapd-2.28.5/interfaces/builtin/desktop_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/desktop_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,11 +20,17 @@ package builtin_test import ( + "os" + "path/filepath" + . "gopkg.in/check.v1" + "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/apparmor" "github.com/snapcore/snapd/interfaces/builtin" + "github.com/snapcore/snapd/interfaces/mount" + "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/testutil" ) @@ -56,6 +62,10 @@ s.coreSlot = MockSlot(c, desktopCoreYaml, nil, "desktop") } +func (s *DesktopInterfaceSuite) TearDownTest(c *C) { + dirs.SetRootDir("/") +} + func (s *DesktopInterfaceSuite) TestName(c *C) { c.Assert(s.iface.Name(), Equals, "desktop") } @@ -91,6 +101,45 @@ c.Assert(spec.SecurityTags(), HasLen, 0) } +func (s *DesktopInterfaceSuite) TestMountSpec(c *C) { + tmpdir := c.MkDir() + dirs.SetRootDir(tmpdir) + c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/usr/share/fonts"), 0777), IsNil) + c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/usr/local/share/fonts"), 0777), IsNil) + c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/var/cache/fontconfig"), 0777), IsNil) + + restore := release.MockOnClassic(false) + defer restore() + + // On all-snaps systems, no mount entries are added + spec := &mount.Specification{} + c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.coreSlot, nil), IsNil) + c.Check(spec.MountEntries(), HasLen, 0) + + // On classic systems, a number of font related directories + // are bind mounted from the host system if they exist. + restore = release.MockOnClassic(true) + defer restore() + spec = &mount.Specification{} + c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.coreSlot, nil), IsNil) + + entries := spec.MountEntries() + c.Assert(entries, HasLen, 3) + + const hostfs = "/var/lib/snapd/hostfs" + c.Check(entries[0].Name, Equals, hostfs+dirs.SystemFontsDir) + c.Check(entries[0].Dir, Equals, "/usr/share/fonts") + c.Check(entries[0].Options, DeepEquals, []string{"bind", "ro"}) + + c.Check(entries[1].Name, Equals, hostfs+dirs.SystemLocalFontsDir) + c.Check(entries[1].Dir, Equals, "/usr/local/share/fonts") + c.Check(entries[1].Options, DeepEquals, []string{"bind", "ro"}) + + c.Check(entries[2].Name, Equals, hostfs+dirs.SystemFontconfigCacheDir) + c.Check(entries[2].Dir, Equals, "/var/cache/fontconfig") + c.Check(entries[2].Options, DeepEquals, []string{"bind", "ro"}) +} + func (s *DesktopInterfaceSuite) TestStaticInfo(c *C) { si := interfaces.StaticInfoOf(s.iface) c.Assert(si.ImplicitOnCore, Equals, false) diff -Nru snapd-2.28.5/interfaces/builtin/framebuffer.go snapd-2.29.3/interfaces/builtin/framebuffer.go --- snapd-2.28.5/interfaces/builtin/framebuffer.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/framebuffer.go 2017-11-09 12:08:52.000000000 +0000 @@ -37,7 +37,7 @@ /run/udev/data/c29:[0-9]* r, ` -const framebufferConnectedPlugUDev = `KERNEL=="fb[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###"` +var framebufferConnectedPlugUDev = []string{`KERNEL=="fb[0-9]*"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/fuse_support.go snapd-2.29.3/interfaces/builtin/fuse_support.go --- snapd-2.28.5/interfaces/builtin/fuse_support.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/fuse_support.go 2017-11-09 12:08:52.000000000 +0000 @@ -83,7 +83,7 @@ #/{,usr/}bin/fusermount ixr, ` -const fuseSupportConnectedPlugUDev = `KERNEL=="fuse", TAG+="###CONNECTED_SECURITY_TAGS###"` +var fuseSupportConnectedPlugUDev = []string{`KERNEL=="fuse"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/hardware_observe.go snapd-2.29.3/interfaces/builtin/hardware_observe.go --- snapd-2.28.5/interfaces/builtin/hardware_observe.go 2017-09-07 12:33:50.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/hardware_observe.go 2017-10-30 13:27:31.000000000 +0000 @@ -40,6 +40,7 @@ # used by lspci capability sys_admin, /etc/modprobe.d/{,*} r, +/lib/modprobe.d/{,*} r, # files in /sys pertaining to hardware (eg, 'lspci -A linux-sysfs') /sys/{block,bus,class,devices,firmware}/{,**} r, diff -Nru snapd-2.28.5/interfaces/builtin/hardware_random_control.go snapd-2.29.3/interfaces/builtin/hardware_random_control.go --- snapd-2.28.5/interfaces/builtin/hardware_random_control.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/hardware_random_control.go 2017-11-09 12:08:52.000000000 +0000 @@ -45,7 +45,7 @@ /sys/devices/virtual/misc/hw_random/rng_current w, ` -const hardwareRandomControlConnectedPlugUDev = `KERNEL=="hwrng", TAG+="###CONNECTED_SECURITY_TAGS###"` +var hardwareRandomControlConnectedPlugUDev = []string{`KERNEL=="hwrng"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/hardware_random_observe.go snapd-2.29.3/interfaces/builtin/hardware_random_observe.go --- snapd-2.28.5/interfaces/builtin/hardware_random_observe.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/hardware_random_observe.go 2017-11-09 12:08:52.000000000 +0000 @@ -40,7 +40,7 @@ /sys/devices/virtual/misc/hw_random/rng_{available,current} r, ` -const hardwareRandomObserveConnectedPlugUDev = `KERNEL=="hwrng", TAG+="###CONNECTED_SECURITY_TAGS###"` +var hardwareRandomObserveConnectedPlugUDev = []string{`KERNEL=="hwrng"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/hidraw.go snapd-2.29.3/interfaces/builtin/hidraw.go --- snapd-2.28.5/interfaces/builtin/hidraw.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/hidraw.go 2017-11-09 12:08:52.000000000 +0000 @@ -1,7 +1,7 @@ // -*- Mode: Go; indent-tabs-mode: t -*- /* - * Copyright (C) 2016 Canonical Ltd + * Copyright (C) 2016-2017 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 @@ -141,7 +141,7 @@ return nil } - // Path to fixed device node (no udev tagging) + // Path to fixed device node path, pathOk := slot.Attrs["path"].(string) if !pathOk { return nil @@ -153,17 +153,30 @@ } func (iface *hidrawInterface) UDevConnectedPlug(spec *udev.Specification, plug *interfaces.Plug, plugAttrs map[string]interface{}, slot *interfaces.Slot, slotAttrs map[string]interface{}) error { + hasOnlyPath := true + if iface.hasUsbAttrs(slot) { + hasOnlyPath = false + } + usbVendor, vOk := slot.Attrs["usb-vendor"].(int64) - if !vOk { + if !vOk && !hasOnlyPath { return nil } usbProduct, pOk := slot.Attrs["usb-product"].(int64) - if !pOk { + if !pOk && !hasOnlyPath { return nil } - for appName := range plug.Apps { - tag := udevSnapSecurityName(plug.Snap.Name(), appName) - spec.AddSnippet(udevUsbDeviceSnippet("hidraw", usbVendor, usbProduct, "TAG", tag)) + + path, pathOk := slot.Attrs["path"].(string) + if !pathOk && hasOnlyPath { + return nil + } + + if hasOnlyPath { + spec.TagDevice(fmt.Sprintf(`SUBSYSTEM=="hidraw", KERNEL=="%s"`, strings.TrimPrefix(path, "/dev/"))) + } else { + spec.TagDevice(fmt.Sprintf(`IMPORT{builtin}="usb_id" +SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="%04x", ATTRS{idProduct}=="%04x"`, usbVendor, usbProduct)) } return nil } diff -Nru snapd-2.28.5/interfaces/builtin/hidraw_test.go snapd-2.29.3/interfaces/builtin/hidraw_test.go --- snapd-2.28.5/interfaces/builtin/hidraw_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/hidraw_test.go 2017-11-03 05:48:53.000000000 +0000 @@ -53,6 +53,7 @@ // Consuming Snap testPlugPort1 *interfaces.Plug testPlugPort2 *interfaces.Plug + testPlugPort3 *interfaces.Plug } var _ = Suite(&HidrawInterfaceSuite{ @@ -133,6 +134,8 @@ interface: hidraw plug-for-device-2: interface: hidraw + plug-for-device-3: + interface: hidraw apps: app-accessing-1-device: @@ -141,9 +144,13 @@ app-accessing-2-devices: command: bar plugs: [plug-for-device-1, plug-for-device-2] + app-accessing-3rd-device: + command: baz + plugs: [plug-for-device-3] `, nil) s.testPlugPort1 = &interfaces.Plug{PlugInfo: consumingSnapInfo.Plugs["plug-for-device-1"]} s.testPlugPort2 = &interfaces.Plug{PlugInfo: consumingSnapInfo.Plugs["plug-for-device-2"]} + s.testPlugPort3 = &interfaces.Plug{PlugInfo: consumingSnapInfo.Plugs["plug-for-device-3"]} } func (s *HidrawInterfaceSuite) TestName(c *C) { @@ -201,24 +208,31 @@ } func (s *HidrawInterfaceSuite) TestConnectedPlugUDevSnippets(c *C) { + // add the plug for the slot with just path spec := &udev.Specification{} c.Assert(spec.AddConnectedPlug(s.iface, s.testPlugPort1, nil, s.testSlot1, nil), IsNil) - c.Assert(spec.Snippets(), HasLen, 0) - - expectedSnippet1 := `IMPORT{builtin}="usb_id" -SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0001", TAG+="snap_client-snap_app-accessing-2-devices"` - c.Assert(spec.AddConnectedPlug(s.iface, s.testPlugPort1, nil, s.testUDev1, nil), IsNil) c.Assert(spec.Snippets(), HasLen, 1) snippet := spec.Snippets()[0] + expectedSnippet1 := `SUBSYSTEM=="hidraw", KERNEL=="hidraw0", TAG+="snap_client-snap_app-accessing-2-devices"` c.Assert(snippet, Equals, expectedSnippet1) + // add the plug for the first slot with vendor and product ids + spec = &udev.Specification{} + c.Assert(spec.AddConnectedPlug(s.iface, s.testPlugPort1, nil, s.testUDev1, nil), IsNil) + c.Assert(spec.Snippets(), HasLen, 1) + snippet = spec.Snippets()[0] expectedSnippet2 := `IMPORT{builtin}="usb_id" -SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="ffff", TAG+="snap_client-snap_app-accessing-2-devices"` +SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0001", TAG+="snap_client-snap_app-accessing-2-devices"` + c.Assert(snippet, Equals, expectedSnippet2) + + // add the plug for the second slot with vendor and product ids spec = &udev.Specification{} c.Assert(spec.AddConnectedPlug(s.iface, s.testPlugPort2, nil, s.testUDev2, nil), IsNil) c.Assert(spec.Snippets(), HasLen, 1) snippet = spec.Snippets()[0] - c.Assert(snippet, Equals, expectedSnippet2) + expectedSnippet3 := `IMPORT{builtin}="usb_id" +SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="ffff", TAG+="snap_client-snap_app-accessing-2-devices"` + c.Assert(snippet, Equals, expectedSnippet3) } func (s *HidrawInterfaceSuite) TestConnectedPlugAppArmorSnippets(c *C) { @@ -247,6 +261,34 @@ c.Assert(snippet, DeepEquals, expectedSnippet3, Commentf("\nexpected:\n%s\nfound:\n%s", expectedSnippet3, snippet)) } +func (s *HidrawInterfaceSuite) TestConnectedPlugUDevSnippetsForPath(c *C) { + expectedSnippet1 := `SUBSYSTEM=="hidraw", KERNEL=="hidraw0", TAG+="snap_client-snap_app-accessing-2-devices"` + udevSpec := &udev.Specification{} + err := udevSpec.AddConnectedPlug(s.iface, s.testPlugPort1, nil, s.testSlot1, nil) + c.Assert(err, IsNil) + c.Assert(udevSpec.Snippets(), HasLen, 1) + snippet := udevSpec.Snippets()[0] + c.Assert(snippet, DeepEquals, expectedSnippet1, Commentf("\nexpected:\n%s\nfound:\n%s", expectedSnippet1, snippet)) + + expectedSnippet2 := `IMPORT{builtin}="usb_id" +SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0001", TAG+="snap_client-snap_app-accessing-2-devices"` + udevSpec = &udev.Specification{} + err = udevSpec.AddConnectedPlug(s.iface, s.testPlugPort1, nil, s.testUDev1, nil) + c.Assert(err, IsNil) + c.Assert(udevSpec.Snippets(), HasLen, 1) + snippet = udevSpec.Snippets()[0] + c.Assert(snippet, DeepEquals, expectedSnippet2, Commentf("\nexpected:\n%s\nfound:\n%s", expectedSnippet2, snippet)) + + expectedSnippet3 := `IMPORT{builtin}="usb_id" +SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="ffff", TAG+="snap_client-snap_app-accessing-2-devices"` + udevSpec = &udev.Specification{} + err = udevSpec.AddConnectedPlug(s.iface, s.testPlugPort2, nil, s.testUDev2, nil) + c.Assert(err, IsNil) + c.Assert(udevSpec.Snippets(), HasLen, 1) + snippet = udevSpec.Snippets()[0] + c.Assert(snippet, DeepEquals, expectedSnippet3, Commentf("\nexpected:\n%s\nfound:\n%s", expectedSnippet3, snippet)) +} + func (s *HidrawInterfaceSuite) TestInterfaces(c *C) { c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface) } diff -Nru snapd-2.28.5/interfaces/builtin/home.go snapd-2.29.3/interfaces/builtin/home.go --- snapd-2.28.5/interfaces/builtin/home.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/home.go 2017-10-30 13:27:31.000000000 +0000 @@ -42,12 +42,13 @@ # Allow read/write access to all files in @{HOME}, except snap application # data in @{HOME}/snaps and toplevel hidden directories in @{HOME}. -owner @{HOME}/[^s.]** rwk, -owner @{HOME}/s[^n]** rwk, -owner @{HOME}/sn[^a]** rwk, -owner @{HOME}/sna[^p]** rwk, +owner @{HOME}/[^s.]** rwklix, +owner @{HOME}/s[^n]** rwklix, +owner @{HOME}/sn[^a]** rwklix, +owner @{HOME}/sna[^p]** rwklix, +owner @{HOME}/snap[^/]** rwklix, # Allow creating a few files not caught above -owner @{HOME}/{s,sn,sna}{,/} rwk, +owner @{HOME}/{s,sn,sna}{,/} rwklix, # Allow access to gvfs mounts for files owned by the user (including hidden # files; only allow writes to files, not the mount point). diff -Nru snapd-2.28.5/interfaces/builtin/i2c.go snapd-2.29.3/interfaces/builtin/i2c.go --- snapd-2.28.5/interfaces/builtin/i2c.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/i2c.go 2017-11-09 12:08:52.000000000 +0000 @@ -48,8 +48,6 @@ /sys/devices/platform/{*,**.i2c}/%s/** rw, ` -const i2cConnectedPlugUDev = `KERNEL=="%s", TAG+="%s"` - // The type for i2c interface type i2cInterface struct{} @@ -111,11 +109,7 @@ if !pathOk { return nil } - const pathPrefix = "/dev/" - for appName := range plug.Apps { - tag := udevSnapSecurityName(plug.Snap.Name(), appName) - spec.AddSnippet(fmt.Sprintf(i2cConnectedPlugUDev, strings.TrimPrefix(path, pathPrefix), tag)) - } + spec.TagDevice(fmt.Sprintf(`KERNEL=="%s"`, strings.TrimPrefix(path, "/dev/"))) return nil } diff -Nru snapd-2.28.5/interfaces/builtin/iio.go snapd-2.29.3/interfaces/builtin/iio.go --- snapd-2.28.5/interfaces/builtin/iio.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/iio.go 2017-11-09 12:08:52.000000000 +0000 @@ -51,8 +51,6 @@ /sys/devices/**/###IIO_DEVICE_NAME###/** rwk, ` -const iioConnectedPlugUDev = `KERNEL=="%s", TAG+="%s"` - // The type for iio interface type iioInterface struct{} @@ -123,11 +121,7 @@ if !pathOk { return nil } - const pathPrefix = "/dev/" - for appName := range plug.Apps { - tag := udevSnapSecurityName(plug.Snap.Name(), appName) - spec.AddSnippet(fmt.Sprintf(iioConnectedPlugUDev, strings.TrimPrefix(path, pathPrefix), tag)) - } + spec.TagDevice(fmt.Sprintf(`KERNEL=="%s"`, strings.TrimPrefix(path, "/dev/"))) return nil } diff -Nru snapd-2.28.5/interfaces/builtin/io_ports_control.go snapd-2.29.3/interfaces/builtin/io_ports_control.go --- snapd-2.28.5/interfaces/builtin/io_ports_control.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/io_ports_control.go 2017-11-09 12:08:52.000000000 +0000 @@ -47,7 +47,8 @@ ioperm iopl ` -const ioPortsControlConnectedPlugUDev = `KERNEL=="port", TAG+="###CONNECTED_SECURITY_TAGS###"` + +var ioPortsControlConnectedPlugUDev = []string{`KERNEL=="port"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/joystick.go snapd-2.29.3/interfaces/builtin/joystick.go --- snapd-2.28.5/interfaces/builtin/joystick.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/joystick.go 2017-11-09 12:08:52.000000000 +0000 @@ -38,7 +38,7 @@ /run/udev/data/c13:{[0-9],[12][0-9],3[01]} r, ` -const joystickConnectedPlugUDev = `KERNEL=="js[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###"` +var joystickConnectedPlugUDev = []string{`KERNEL=="js[0-9]*"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/kernel_module_control.go snapd-2.29.3/interfaces/builtin/kernel_module_control.go --- snapd-2.28.5/interfaces/builtin/kernel_module_control.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/kernel_module_control.go 2017-11-09 12:08:52.000000000 +0000 @@ -60,7 +60,8 @@ finit_module delete_module ` -const kernelModuleControlConnectedPlugUDev = `KERNEL=="mem", TAG+="###CONNECTED_SECURITY_TAGS###"` + +var kernelModuleControlConnectedPlugUDev = []string{`KERNEL=="mem"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/kvm.go snapd-2.29.3/interfaces/builtin/kvm.go --- snapd-2.28.5/interfaces/builtin/kvm.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/kvm.go 2017-11-09 12:08:52.000000000 +0000 @@ -36,7 +36,7 @@ /dev/kvm rw, ` -const kvmConnectedPlugUDev = `KERNEL=="kvm", TAG+="###CONNECTED_SECURITY_TAGS###"` +var kvmConnectedPlugUDev = []string{`KERNEL=="kvm"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/mir.go snapd-2.29.3/interfaces/builtin/mir.go --- snapd-2.28.5/interfaces/builtin/mir.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/mir.go 2017-11-09 12:08:52.000000000 +0000 @@ -20,7 +20,6 @@ package builtin import ( - "fmt" "strings" "github.com/snapcore/snapd/interfaces" @@ -87,20 +86,6 @@ unix (receive, send) type=seqpacket addr=none peer=(label=###SLOT_SECURITY_TAGS###), /run/mir_socket rw, /run/user/[0-9]*/mir_socket rw, - -# Lttng tracing is very noisy and should not be allowed by confined apps. Can -# safely deny. LP: #1260491 -deny /{dev,run,var/run}/shm/lttng-ust-* rw, -` - -const mirPermanentSlotUdev = ` -KERNEL=="tty[0-9]*", TAG+="%[1]s" - -# input devices -KERNEL=="mice", TAG+="%[1]s" -KERNEL=="mouse[0-9]*", TAG+="%[1]s" -KERNEL=="event[0-9]*", TAG+="%[1]s" -KERNEL=="ts[0-9]*", TAG+="%[1]s" ` type mirInterface struct{} @@ -143,10 +128,11 @@ } func (iface *mirInterface) UDevPermanentSlot(spec *udev.Specification, slot *interfaces.Slot) error { - for appName := range slot.Apps { - tag := udevSnapSecurityName(slot.Snap.Name(), appName) - spec.AddSnippet(fmt.Sprintf(mirPermanentSlotUdev, tag)) - } + spec.TagDevice(`KERNEL=="tty[0-9]*"`) + spec.TagDevice(`KERNEL=="mice"`) + spec.TagDevice(`KERNEL=="mouse[0-9]*"`) + spec.TagDevice(`KERNEL=="event[0-9]*"`) + spec.TagDevice(`KERNEL=="ts[0-9]*"`) return nil } diff -Nru snapd-2.28.5/interfaces/builtin/mir_test.go snapd-2.29.3/interfaces/builtin/mir_test.go --- snapd-2.28.5/interfaces/builtin/mir_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/mir_test.go 2017-11-09 12:08:52.000000000 +0000 @@ -139,8 +139,8 @@ func (s *MirInterfaceSuite) TestUDevSpec(c *C) { udevSpec := &udev.Specification{} c.Assert(udevSpec.AddPermanentSlot(s.iface, s.coreSlot), IsNil) - c.Assert(udevSpec.Snippets(), HasLen, 1) - c.Assert(udevSpec.Snippets()[0], testutil.Contains, `KERNEL=="event[0-9]*", TAG+="snap_mir-server_mir"`) + c.Assert(udevSpec.Snippets(), HasLen, 5) + c.Assert(udevSpec.Snippets(), testutil.Contains, `KERNEL=="event[0-9]*", TAG+="snap_mir-server_mir"`) } func (s *MirInterfaceSuite) TestInterfaces(c *C) { diff -Nru snapd-2.28.5/interfaces/builtin/modem_manager.go snapd-2.29.3/interfaces/builtin/modem_manager.go --- snapd-2.28.5/interfaces/builtin/modem_manager.go 2017-09-15 15:05:07.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/modem_manager.go 2017-11-09 12:08:52.000000000 +0000 @@ -1188,10 +1188,6 @@ LABEL="mm_candidate_end" ` -const modemManagerPermanentSlotUDevTag = ` -KERNEL=="tty[A-Z]*[0-9]*|cdc-wdm[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###" -` - type modemManagerInterface struct{} func (iface *modemManagerInterface) Name() string { @@ -1233,13 +1229,8 @@ } func (iface *modemManagerInterface) UDevPermanentSlot(spec *udev.Specification, slot *interfaces.Slot) error { - old := "###CONNECTED_SECURITY_TAGS###" - udevRule := modemManagerPermanentSlotUDev - for appName := range slot.Apps { - tag := udevSnapSecurityName(slot.Snap.Name(), appName) - udevRule += strings.Replace(modemManagerPermanentSlotUDevTag, old, tag, -1) - } - spec.AddSnippet(udevRule) + spec.AddSnippet(modemManagerPermanentSlotUDev) + spec.TagDevice(`KERNEL=="tty[A-Z]*[0-9]*|cdc-wdm[0-9]*"`) return nil } diff -Nru snapd-2.28.5/interfaces/builtin/modem_manager_test.go snapd-2.29.3/interfaces/builtin/modem_manager_test.go --- snapd-2.28.5/interfaces/builtin/modem_manager_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/modem_manager_test.go 2017-11-09 12:08:52.000000000 +0000 @@ -207,9 +207,9 @@ udevSpec := &udev.Specification{} c.Assert(udevSpec.AddPermanentSlot(s.iface, s.slot), IsNil) - c.Assert(udevSpec.Snippets(), HasLen, 1) + c.Assert(udevSpec.Snippets(), HasLen, 2) c.Assert(udevSpec.Snippets()[0], testutil.Contains, `SUBSYSTEMS=="usb"`) - c.Assert(udevSpec.Snippets()[0], testutil.Contains, `KERNEL=="tty[A-Z]*[0-9]*|cdc-wdm[0-9]*", TAG+="snap_modem-manager_mm"`) + c.Assert(udevSpec.Snippets(), testutil.Contains, `KERNEL=="tty[A-Z]*[0-9]*|cdc-wdm[0-9]*", TAG+="snap_modem-manager_mm"`) } func (s *ModemManagerInterfaceSuite) TestPermanentSlotDBus(c *C) { diff -Nru snapd-2.28.5/interfaces/builtin/network_control.go snapd-2.29.3/interfaces/builtin/network_control.go --- snapd-2.28.5/interfaces/builtin/network_control.go 2017-10-12 16:34:56.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/network_control.go 2017-11-09 12:08:52.000000000 +0000 @@ -255,10 +255,10 @@ * We only need to tag /dev/net/tun since the tap[0-9]* and tun[0-9]* devices * are virtual and don't show up in /dev */ -const networkControlConnectedPlugUDev = ` -KERNEL=="rfkill", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="tun", TAG+="###CONNECTED_SECURITY_TAGS###" -` +var networkControlConnectedPlugUDev = []string{ + `KERNEL=="rfkill"`, + `KERNEL=="tun"`, +} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/network_control_test.go snapd-2.29.3/interfaces/builtin/network_control_test.go --- snapd-2.28.5/interfaces/builtin/network_control_test.go 2017-10-12 16:34:56.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/network_control_test.go 2017-11-09 12:08:52.000000000 +0000 @@ -94,8 +94,8 @@ func (s *NetworkControlInterfaceSuite) TestUDevSpec(c *C) { spec := &udev.Specification{} c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.slot, nil), IsNil) - c.Assert(spec.Snippets(), HasLen, 1) - c.Assert(spec.Snippets()[0], testutil.Contains, `KERNEL=="tun", TAG+="snap_consumer_app"`) + c.Assert(spec.Snippets(), HasLen, 2) + c.Assert(spec.Snippets(), testutil.Contains, `KERNEL=="tun", TAG+="snap_consumer_app"`) } func (s *NetworkControlInterfaceSuite) TestStaticInfo(c *C) { diff -Nru snapd-2.28.5/interfaces/builtin/network.go snapd-2.29.3/interfaces/builtin/network.go --- snapd-2.28.5/interfaces/builtin/network.go 2017-09-15 15:05:07.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/network.go 2017-10-23 06:17:27.000000000 +0000 @@ -57,6 +57,9 @@ @{PROC}/sys/net/core/somaxconn r, @{PROC}/sys/net/ipv4/tcp_fastopen r, + +# Allow using netcat as client +/{,usr/}bin/nc{,.openbsd} ixr, ` // http://bazaar.launchpad.net/~ubuntu-security/ubuntu-core-security/trunk/view/head:/data/seccomp/policygroups/ubuntu-core/16.04/network diff -Nru snapd-2.28.5/interfaces/builtin/network_manager.go snapd-2.29.3/interfaces/builtin/network_manager.go --- snapd-2.28.5/interfaces/builtin/network_manager.go 2017-09-15 15:05:07.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/network_manager.go 2017-11-09 12:08:52.000000000 +0000 @@ -414,8 +414,6 @@ 2048 ` -const networkManagerPermanentSlotUdev = `KERNEL=="rfkill", TAG+="###CONNECTED_SECURITY_TAGS###"` - type networkManagerInterface struct{} func (iface *networkManagerInterface) Name() string { @@ -469,12 +467,7 @@ } func (iface *networkManagerInterface) UDevPermanentSlot(spec *udev.Specification, slot *interfaces.Slot) error { - old := "###CONNECTED_SECURITY_TAGS###" - for appName := range slot.Apps { - tag := udevSnapSecurityName(slot.Snap.Name(), appName) - udevRule := strings.Replace(networkManagerPermanentSlotUdev, old, tag, -1) - spec.AddSnippet(udevRule) - } + spec.TagDevice(`KERNEL=="rfkill"`) return nil } diff -Nru snapd-2.28.5/interfaces/builtin/network_status.go snapd-2.29.3/interfaces/builtin/network_status.go --- snapd-2.28.5/interfaces/builtin/network_status.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/network_status.go 2017-11-09 07:19:00.000000000 +0000 @@ -53,6 +53,13 @@ dbus (bind) bus=system name="com.ubuntu.connectivity1.NetworkingStatus", + +# allow queries from unconfined +dbus (receive) + bus=system + path=/com/ubuntu/connectivity1/NetworkingStatus{,/**} + interface=org.freedesktop.DBus.* + peer=(label=unconfined), ` const networkStatusConnectedSlotAppArmor = ` diff -Nru snapd-2.28.5/interfaces/builtin/ofono.go snapd-2.29.3/interfaces/builtin/ofono.go --- snapd-2.28.5/interfaces/builtin/ofono.go 2017-09-15 15:05:07.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/ofono.go 2017-11-09 12:08:52.000000000 +0000 @@ -289,20 +289,6 @@ LABEL="ofono_speedup_end" ` -/* - 1.Linux modem drivers set up the modem device /dev/modem as a symbolic link - to the actual device to /dev/ttyS* - 2./dev/socket/rild is just a socket, not device node created by rild daemon. - Similar case for chnlat*. - So we intetionally skipped modem, rild and chnlat. -*/ -const ofonoPermanentSlotUDevTag = ` -KERNEL=="tty[A-Z]*[0-9]*|cdc-wdm[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="tun", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="tun[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="dsp", TAG+="###CONNECTED_SECURITY_TAGS###" -` - type ofonoInterface struct{} func (iface *ofonoInterface) Name() string { @@ -334,20 +320,24 @@ return nil } -func (iface *ofonoInterface) DBusPermanentSlot(spec *dbus.Specification, plug *interfaces.Plug, slot *interfaces.Slot) error { +func (iface *ofonoInterface) DBusPermanentSlot(spec *dbus.Specification, slot *interfaces.Slot) error { spec.AddSnippet(ofonoPermanentSlotDBus) return nil } func (iface *ofonoInterface) UDevPermanentSlot(spec *udev.Specification, slot *interfaces.Slot) error { - old := "###CONNECTED_SECURITY_TAGS###" - udevRule := ofonoPermanentSlotUDev - for appName := range slot.Apps { - tag := udevSnapSecurityName(slot.Snap.Name(), appName) - udevRule += strings.Replace(ofonoPermanentSlotUDevTag, old, tag, -1) - spec.AddSnippet(udevRule) - } - spec.AddSnippet(udevRule) + spec.AddSnippet(ofonoPermanentSlotUDev) + /* + 1.Linux modem drivers set up the modem device /dev/modem as a symbolic link + to the actual device to /dev/ttyS* + 2./dev/socket/rild is just a socket, not device node created by rild daemon. + Similar case for chnlat*. + So we intetionally skipped modem, rild and chnlat. + */ + spec.TagDevice(`KERNEL=="tty[A-Z]*[0-9]*|cdc-wdm[0-9]*"`) + spec.TagDevice(`KERNEL=="tun"`) + spec.TagDevice(`KERNEL=="tun[0-9]*"`) + spec.TagDevice(`KERNEL=="dsp"`) return nil } diff -Nru snapd-2.28.5/interfaces/builtin/ofono_test.go snapd-2.29.3/interfaces/builtin/ofono_test.go --- snapd-2.28.5/interfaces/builtin/ofono_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/ofono_test.go 2017-11-09 12:08:52.000000000 +0000 @@ -200,9 +200,9 @@ func (s *OfonoInterfaceSuite) TestPermanentSlotSnippetUDev(c *C) { spec := &udev.Specification{} c.Assert(spec.AddPermanentSlot(s.iface, s.slot), IsNil) - c.Assert(spec.Snippets(), HasLen, 1) + c.Assert(spec.Snippets(), HasLen, 5) c.Assert(spec.Snippets()[0], testutil.Contains, `LABEL="ofono_isi_end"`) - c.Assert(spec.Snippets()[0], testutil.Contains, `KERNEL=="tty[A-Z]*[0-9]*|cdc-wdm[0-9]*", TAG+="snap_ofono_app"`) + c.Assert(spec.Snippets(), testutil.Contains, `KERNEL=="tty[A-Z]*[0-9]*|cdc-wdm[0-9]*", TAG+="snap_ofono_app"`) } func (s *OfonoInterfaceSuite) TestInterfaces(c *C) { diff -Nru snapd-2.28.5/interfaces/builtin/opengl.go snapd-2.29.3/interfaces/builtin/opengl.go --- snapd-2.28.5/interfaces/builtin/opengl.go 2017-10-11 17:40:25.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/opengl.go 2017-11-09 12:08:52.000000000 +0000 @@ -35,6 +35,19 @@ /var/lib/snapd/lib/gl/ r, /var/lib/snapd/lib/gl/** rm, + # Supports linux-driver-management from Solus (staged symlink trees into libdirs) + /var/lib/snapd/hostfs/{,usr/}lib{,32,64,x32}/{,@{multiarch}/}glx-provider/**.so{,.*} rm, + + # Bi-arch distribution nvidia support + /var/lib/snapd/hostfs/{,usr/}lib{,32,64,x32}/{,@{multiarch}/}libcuda*.so{,.*} rm, + /var/lib/snapd/hostfs/{,usr/}lib{,32,64,x32}/{,@{multiarch}/}libnvidia*.so{,.*} rm, + /var/lib/snapd/hostfs/{,usr/}lib{,32,64,x32}/{,@{multiarch}/}libnvcuvid.so{,.*} rm, + /var/lib/snapd/hostfs/{,usr/}lib{,32,64,x32}/{,@{multiarch}/}lib{GL,EGL}*nvidia.so{,.*} rm, + /var/lib/snapd/hostfs/{,usr/}lib{,32,64,x32}/{,@{multiarch}/}libGLdispatch.so{,.*} rm, + + # Main bi-arch GL libraries + /var/lib/snapd/hostfs/{,usr/}lib{,32,64,x32}/{,@{multiarch}/}lib{GL,EGL}.so{,.*} rm, + /dev/dri/ r, /dev/dri/card0 rw, # nvidia @@ -70,10 +83,10 @@ // The nvidia modules don't use sysfs (therefore they can't be udev tagged) and // will be added by snap-confine. -const openglConnectedPlugUDev = ` -SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="vchiq", TAG+="###CONNECTED_SECURITY_TAGS###" -` +var openglConnectedPlugUDev = []string{ + `SUBSYSTEM=="drm", KERNEL=="card[0-9]*"`, + `KERNEL=="vchiq"`, +} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/opengl_test.go snapd-2.29.3/interfaces/builtin/opengl_test.go --- snapd-2.28.5/interfaces/builtin/opengl_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/opengl_test.go 2017-11-09 12:08:52.000000000 +0000 @@ -86,8 +86,8 @@ func (s *OpenglInterfaceSuite) TestUDevSpec(c *C) { spec := &udev.Specification{} c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.slot, nil), IsNil) - c.Assert(spec.Snippets(), HasLen, 1) - c.Assert(spec.Snippets()[0], testutil.Contains, `SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="snap_consumer_app"`) + c.Assert(spec.Snippets(), HasLen, 2) + c.Assert(spec.Snippets(), testutil.Contains, `SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="snap_consumer_app"`) } func (s *OpenglInterfaceSuite) TestStaticInfo(c *C) { diff -Nru snapd-2.28.5/interfaces/builtin/optical_drive.go snapd-2.29.3/interfaces/builtin/optical_drive.go --- snapd-2.28.5/interfaces/builtin/optical_drive.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/optical_drive.go 2017-11-09 12:08:52.000000000 +0000 @@ -36,10 +36,10 @@ /run/udev/data/b11:[0-9]* r, ` -const opticalDriveConnectedPlugUDev = ` -KERNEL=="sr[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="scd[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###" -` +var opticalDriveConnectedPlugUDev = []string{ + `KERNEL=="sr[0-9]*"`, + `KERNEL=="scd[0-9]*"`, +} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/optical_drive_test.go snapd-2.29.3/interfaces/builtin/optical_drive_test.go --- snapd-2.28.5/interfaces/builtin/optical_drive_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/optical_drive_test.go 2017-11-09 12:08:52.000000000 +0000 @@ -86,8 +86,8 @@ func (s *OpticalDriveInterfaceSuite) TestUDevSpec(c *C) { spec := &udev.Specification{} c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.slot, nil), IsNil) - c.Assert(spec.Snippets(), HasLen, 1) - c.Assert(spec.Snippets()[0], testutil.Contains, `KERNEL=="sr[0-9]*", TAG+="snap_consumer_app"`) + c.Assert(spec.Snippets(), HasLen, 2) + c.Assert(spec.Snippets(), testutil.Contains, `KERNEL=="sr[0-9]*", TAG+="snap_consumer_app"`) } func (s *OpticalDriveInterfaceSuite) TestStaticInfo(c *C) { diff -Nru snapd-2.28.5/interfaces/builtin/physical_memory_control.go snapd-2.29.3/interfaces/builtin/physical_memory_control.go --- snapd-2.28.5/interfaces/builtin/physical_memory_control.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/physical_memory_control.go 2017-11-09 12:08:52.000000000 +0000 @@ -41,7 +41,7 @@ /dev/mem rw, ` -const physicalMemoryControlConnectedPlugUDev = `KERNEL=="mem", TAG+="###CONNECTED_SECURITY_TAGS###"` +var physicalMemoryControlConnectedPlugUDev = []string{`KERNEL=="mem"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/physical_memory_observe.go snapd-2.29.3/interfaces/builtin/physical_memory_observe.go --- snapd-2.28.5/interfaces/builtin/physical_memory_observe.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/physical_memory_observe.go 2017-11-09 12:08:52.000000000 +0000 @@ -37,7 +37,7 @@ /dev/mem r, ` -const physicalMemoryObserveConnectedPlugUDev = `KERNEL=="mem", TAG+="###CONNECTED_SECURITY_TAGS###"` +var physicalMemoryObserveConnectedPlugUDev = []string{`KERNEL=="mem"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/ppp.go snapd-2.29.3/interfaces/builtin/ppp.go --- snapd-2.28.5/interfaces/builtin/ppp.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/ppp.go 2017-11-09 12:08:52.000000000 +0000 @@ -55,10 +55,10 @@ "ppp_generic", } -const pppConnectedPlugUDev = ` -KERNEL=="ppp", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="tty[A-Z]*[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###" -` +var pppConnectedPlugUDev = []string{ + `KERNEL=="ppp"`, + `KERNEL=="tty[A-Z]*[0-9]*"`, +} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/ppp_test.go snapd-2.29.3/interfaces/builtin/ppp_test.go --- snapd-2.28.5/interfaces/builtin/ppp_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/ppp_test.go 2017-11-09 12:08:52.000000000 +0000 @@ -96,8 +96,8 @@ func (s *PppInterfaceSuite) TestUDevSpec(c *C) { spec := &udev.Specification{} c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.slot, nil), IsNil) - c.Assert(spec.Snippets(), HasLen, 1) - c.Assert(spec.Snippets()[0], testutil.Contains, `KERNEL=="ppp", TAG+="snap_consumer_app"`) + c.Assert(spec.Snippets(), HasLen, 2) + c.Assert(spec.Snippets(), testutil.Contains, `KERNEL=="ppp", TAG+="snap_consumer_app"`) } func (s *PppInterfaceSuite) TestStaticInfo(c *C) { diff -Nru snapd-2.28.5/interfaces/builtin/pulseaudio.go snapd-2.29.3/interfaces/builtin/pulseaudio.go --- snapd-2.28.5/interfaces/builtin/pulseaudio.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/pulseaudio.go 2017-11-09 12:08:52.000000000 +0000 @@ -20,8 +20,6 @@ package builtin import ( - "strings" - "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/apparmor" "github.com/snapcore/snapd/interfaces/seccomp" @@ -129,12 +127,6 @@ socket AF_NETLINK - NETLINK_KOBJECT_UEVENT ` -const pulseaudioPermanentSlotUdev = ` -KERNEL=="controlC[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="pcmC[0-9]*D[0-9]*[cp]", TAG+="###CONNECTED_SECURITY_TAGS###" -KERNEL=="timer", TAG+="###CONNECTED_SECURITY_TAGS###" -` - type pulseAudioInterface struct{} func (iface *pulseAudioInterface) Name() string { @@ -158,12 +150,9 @@ } func (iface *pulseAudioInterface) UDevPermanentSlot(spec *udev.Specification, slot *interfaces.Slot) error { - old := "###CONNECTED_SECURITY_TAGS###" - for appName := range slot.Apps { - tag := udevSnapSecurityName(slot.Snap.Name(), appName) - udevRule := strings.Replace(pulseaudioPermanentSlotUdev, old, tag, -1) - spec.AddSnippet(udevRule) - } + spec.TagDevice(`KERNEL=="controlC[0-9]*"`) + spec.TagDevice(`KERNEL=="pcmC[0-9]*D[0-9]*[cp]"`) + spec.TagDevice(`KERNEL=="timer"`) return nil } diff -Nru snapd-2.28.5/interfaces/builtin/pulseaudio_test.go snapd-2.29.3/interfaces/builtin/pulseaudio_test.go --- snapd-2.28.5/interfaces/builtin/pulseaudio_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/pulseaudio_test.go 2017-11-09 12:08:52.000000000 +0000 @@ -115,8 +115,8 @@ func (s *PulseAudioInterfaceSuite) TestUDev(c *C) { spec := &udev.Specification{} c.Assert(spec.AddPermanentSlot(s.iface, s.coreSlot), IsNil) - c.Assert(spec.Snippets(), HasLen, 1) - c.Assert(spec.Snippets()[0], testutil.Contains, `KERNEL=="pcmC[0-9]*D[0-9]*[cp]", TAG+="snap_pulseaudio_app1"`) + c.Assert(spec.Snippets(), HasLen, 3) + c.Assert(spec.Snippets(), testutil.Contains, `KERNEL=="pcmC[0-9]*D[0-9]*[cp]", TAG+="snap_pulseaudio_app1"`) } func (s *PulseAudioInterfaceSuite) TestInterfaces(c *C) { diff -Nru snapd-2.28.5/interfaces/builtin/raw_usb.go snapd-2.29.3/interfaces/builtin/raw_usb.go --- snapd-2.28.5/interfaces/builtin/raw_usb.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/raw_usb.go 2017-11-09 12:08:52.000000000 +0000 @@ -45,7 +45,7 @@ /run/udev/data/+usb:* r, ` -const rawusbConnectedPlugUDev = `SUBSYSTEMS=="usb", TAG+="###CONNECTED_SECURITY_TAGS###"` +var rawusbConnectedPlugUDev = []string{`SUBSYSTEM=="usb"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/raw_usb_test.go snapd-2.29.3/interfaces/builtin/raw_usb_test.go --- snapd-2.28.5/interfaces/builtin/raw_usb_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/raw_usb_test.go 2017-11-09 07:19:00.000000000 +0000 @@ -87,7 +87,7 @@ spec := &udev.Specification{} c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.slot, nil), IsNil) c.Assert(spec.Snippets(), HasLen, 1) - c.Assert(spec.Snippets()[0], testutil.Contains, `SUBSYSTEMS=="usb", TAG+="snap_consumer_app"`) + c.Assert(spec.Snippets()[0], testutil.Contains, `SUBSYSTEM=="usb", TAG+="snap_consumer_app"`) } func (s *RawUsbInterfaceSuite) TestStaticInfo(c *C) { diff -Nru snapd-2.28.5/interfaces/builtin/removable_media.go snapd-2.29.3/interfaces/builtin/removable_media.go --- snapd-2.28.5/interfaces/builtin/removable_media.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/removable_media.go 2017-10-30 13:27:31.000000000 +0000 @@ -32,6 +32,13 @@ const removableMediaConnectedPlugAppArmor = ` # Description: Can access removable storage filesystems +# Allow read-access to /run/ for navigating to removable media. +/run/ r, + +# Allow read on /run/media/ for navigating to the mount points. While this +# allows enumerating users, this is already allowed via /etc/passwd and getent. +/{,run/}media/ r, + # Mount points could be in /run/media//* or /media//* /{,run/}media/*/ r, /{,run/}media/*/** rw, diff -Nru snapd-2.28.5/interfaces/builtin/serial_port.go snapd-2.29.3/interfaces/builtin/serial_port.go --- snapd-2.28.5/interfaces/builtin/serial_port.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/serial_port.go 2017-11-09 12:08:52.000000000 +0000 @@ -1,7 +1,7 @@ // -*- Mode: Go; indent-tabs-mode: t -*- /* - * Copyright (C) 2016 Canonical Ltd + * Copyright (C) 2016-2017 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 @@ -148,7 +148,7 @@ return nil } - // Path to fixed device node (no udev tagging) + // Path to fixed device node path, pathOk := slot.Attrs["path"].(string) if !pathOk { return nil @@ -159,17 +159,32 @@ } func (iface *serialPortInterface) UDevConnectedPlug(spec *udev.Specification, plug *interfaces.Plug, plugAttrs map[string]interface{}, slot *interfaces.Slot, slotAttrs map[string]interface{}) error { + // For connected plugs, we use vendor and product ids if available, + // otherwise add the kernel device + hasOnlyPath := true + if iface.hasUsbAttrs(slot) { + hasOnlyPath = false + } + usbVendor, vOk := slot.Attrs["usb-vendor"].(int64) - if !vOk { + if !vOk && !hasOnlyPath { return nil } usbProduct, pOk := slot.Attrs["usb-product"].(int64) - if !pOk { + if !pOk && !hasOnlyPath { return nil } - for appName := range plug.Apps { - tag := udevSnapSecurityName(plug.Snap.Name(), appName) - spec.AddSnippet(udevUsbDeviceSnippet("tty", usbVendor, usbProduct, "TAG", tag)) + + path, pathOk := slot.Attrs["path"].(string) + if !pathOk && hasOnlyPath { + return nil + } + + if hasOnlyPath { + spec.TagDevice(fmt.Sprintf(`SUBSYSTEM=="tty", KERNEL=="%s"`, strings.TrimPrefix(path, "/dev/"))) + } else { + spec.TagDevice(fmt.Sprintf(`IMPORT{builtin}="usb_id" +SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="%04x", ATTRS{idProduct}=="%04x"`, usbVendor, usbProduct)) } return nil } diff -Nru snapd-2.28.5/interfaces/builtin/serial_port_test.go snapd-2.29.3/interfaces/builtin/serial_port_test.go --- snapd-2.28.5/interfaces/builtin/serial_port_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/serial_port_test.go 2017-11-03 05:48:53.000000000 +0000 @@ -65,6 +65,7 @@ // Consuming Snap testPlugPort1 *interfaces.Plug testPlugPort2 *interfaces.Plug + testPlugPort3 *interfaces.Plug } var _ = Suite(&SerialPortInterfaceSuite{ @@ -193,6 +194,8 @@ interface: serial-port plug-for-port-2: interface: serial-port + plug-for-port-3: + interface: serial-port apps: app-accessing-1-port: @@ -201,9 +204,13 @@ app-accessing-2-ports: command: bar plugs: [plug-for-port-1, plug-for-port-2] + app-accessing-3rd-port: + command: foo + plugs: [plug-for-port-3] `, nil) s.testPlugPort1 = &interfaces.Plug{PlugInfo: consumingSnapInfo.Plugs["plug-for-port-1"]} s.testPlugPort2 = &interfaces.Plug{PlugInfo: consumingSnapInfo.Plugs["plug-for-port-2"]} + s.testPlugPort3 = &interfaces.Plug{PlugInfo: consumingSnapInfo.Plugs["plug-for-port-3"]} } func (s *SerialPortInterfaceSuite) TestName(c *C) { @@ -264,27 +271,34 @@ } func (s *SerialPortInterfaceSuite) TestConnectedPlugUDevSnippets(c *C) { + // add the plug for the slot with just path spec := &udev.Specification{} err := spec.AddConnectedPlug(s.iface, s.testPlugPort1, nil, s.testSlot1, nil) c.Assert(err, IsNil) - c.Assert(spec.Snippets(), HasLen, 0) + c.Assert(spec.Snippets(), HasLen, 1) + snippet := spec.Snippets()[0] + expectedSnippet1 := `SUBSYSTEM=="tty", KERNEL=="ttyS0", TAG+="snap_client-snap_app-accessing-2-ports"` + c.Assert(snippet, Equals, expectedSnippet1) - expectedSnippet1 := `IMPORT{builtin}="usb_id" -SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0001", TAG+="snap_client-snap_app-accessing-2-ports"` + // add plug for the first slot with product and vendor ids + spec = &udev.Specification{} err = spec.AddConnectedPlug(s.iface, s.testPlugPort1, nil, s.testUDev1, nil) c.Assert(err, IsNil) c.Assert(spec.Snippets(), HasLen, 1) - snippet := spec.Snippets()[0] - c.Assert(snippet, Equals, expectedSnippet1) + snippet = spec.Snippets()[0] + expectedSnippet2 := `IMPORT{builtin}="usb_id" +SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0001", TAG+="snap_client-snap_app-accessing-2-ports"` + c.Assert(snippet, Equals, expectedSnippet2) + // add plug for the first slot with product and vendor ids spec = &udev.Specification{} err = spec.AddConnectedPlug(s.iface, s.testPlugPort2, nil, s.testUDev2, nil) c.Assert(err, IsNil) c.Assert(spec.Snippets(), HasLen, 1) snippet = spec.Snippets()[0] - expectedSnippet2 := `IMPORT{builtin}="usb_id" + expectedSnippet3 := `IMPORT{builtin}="usb_id" SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="ffff", TAG+="snap_client-snap_app-accessing-2-ports"` - c.Assert(snippet, Equals, expectedSnippet2) + c.Assert(snippet, Equals, expectedSnippet3) } func (s *SerialPortInterfaceSuite) TestConnectedPlugAppArmorSnippets(c *C) { @@ -325,6 +339,49 @@ checkConnectedPlugSnippet(s.testPlugPort2, s.testUDev2, expectedSnippet9) } +func (s *SerialPortInterfaceSuite) TestConnectedPlugUDevSnippetsForPath(c *C) { + checkConnectedPlugSnippet := func(plug *interfaces.Plug, slot *interfaces.Slot, expectedSnippet string) { + udevSpec := &udev.Specification{} + err := udevSpec.AddConnectedPlug(s.iface, plug, nil, slot, nil) + c.Assert(err, IsNil) + + c.Assert(udevSpec.Snippets(), HasLen, 1) + snippet := udevSpec.Snippets()[0] + c.Assert(snippet, DeepEquals, expectedSnippet, Commentf("\nexpected:\n%s\nfound:\n%s", expectedSnippet, snippet)) + } + + // these have only path + expectedSnippet1 := `SUBSYSTEM=="tty", KERNEL=="ttyS0", TAG+="snap_client-snap_app-accessing-3rd-port"` + checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot1, expectedSnippet1) + + expectedSnippet2 := `SUBSYSTEM=="tty", KERNEL=="ttyUSB927", TAG+="snap_client-snap_app-accessing-3rd-port"` + checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot2, expectedSnippet2) + + expectedSnippet3 := `SUBSYSTEM=="tty", KERNEL=="ttyS42", TAG+="snap_client-snap_app-accessing-3rd-port"` + checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot3, expectedSnippet3) + + expectedSnippet4 := `SUBSYSTEM=="tty", KERNEL=="ttyO0", TAG+="snap_client-snap_app-accessing-3rd-port"` + checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot4, expectedSnippet4) + + expectedSnippet5 := `SUBSYSTEM=="tty", KERNEL=="ttyACM0", TAG+="snap_client-snap_app-accessing-3rd-port"` + checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot5, expectedSnippet5) + + expectedSnippet6 := `SUBSYSTEM=="tty", KERNEL=="ttyAMA0", TAG+="snap_client-snap_app-accessing-3rd-port"` + checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot6, expectedSnippet6) + + expectedSnippet7 := `SUBSYSTEM=="tty", KERNEL=="ttyXRUSB0", TAG+="snap_client-snap_app-accessing-3rd-port"` + checkConnectedPlugSnippet(s.testPlugPort3, s.testSlot7, expectedSnippet7) + + // these have product and vendor ids + expectedSnippet8 := `IMPORT{builtin}="usb_id" +SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0001", TAG+="snap_client-snap_app-accessing-3rd-port"` + checkConnectedPlugSnippet(s.testPlugPort3, s.testUDev1, expectedSnippet8) + + expectedSnippet9 := `IMPORT{builtin}="usb_id" +SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="ffff", TAG+="snap_client-snap_app-accessing-3rd-port"` + checkConnectedPlugSnippet(s.testPlugPort3, s.testUDev2, expectedSnippet9) +} + func (s *SerialPortInterfaceSuite) TestInterfaces(c *C) { c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface) } diff -Nru snapd-2.28.5/interfaces/builtin/spi.go snapd-2.29.3/interfaces/builtin/spi.go --- snapd-2.28.5/interfaces/builtin/spi.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/spi.go 2017-11-09 12:08:52.000000000 +0000 @@ -91,10 +91,7 @@ if err != nil { return nil } - for appName := range plug.Apps { - tag := udevSnapSecurityName(plug.Snap.Name(), appName) - spec.AddSnippet(fmt.Sprintf(`KERNEL=="%s", TAG+="%s"`, strings.TrimPrefix(path, "/dev/"), tag)) - } + spec.TagDevice(fmt.Sprintf(`KERNEL=="%s"`, strings.TrimPrefix(path, "/dev/"))) return nil } diff -Nru snapd-2.28.5/interfaces/builtin/system_observe.go snapd-2.29.3/interfaces/builtin/system_observe.go --- snapd-2.28.5/interfaces/builtin/system_observe.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/system_observe.go 2017-10-23 06:17:27.000000000 +0000 @@ -66,6 +66,10 @@ @{PROC}/*/{,task/*/}statm r, @{PROC}/*/{,task/*/}status r, +# Allow discovering the os-release of the host +/var/lib/snapd/hostfs/etc/os-release rk, +/var/lib/snapd/hostfs/usr/lib/os-release rk, + #include dbus (send) @@ -82,6 +86,14 @@ interface=org.freedesktop.DBus.Introspectable member=Introspect peer=(label=unconfined), + +# Allow clients to enumerate DBus connection names on common buses +dbus (send) + bus={session,system} + path=/org/freedesktop/DBus + interface=org.freedesktop.DBus + member=ListNames + peer=(label=unconfined), ` const systemObserveConnectedPlugSecComp = ` diff -Nru snapd-2.28.5/interfaces/builtin/time_control.go snapd-2.29.3/interfaces/builtin/time_control.go --- snapd-2.28.5/interfaces/builtin/time_control.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/time_control.go 2017-11-09 12:08:52.000000000 +0000 @@ -92,7 +92,7 @@ /sbin/hwclock ixr, ` -const timeControlConnectedPlugUDev = `SUBSYSTEM=="rtc", TAG+="###CONNECTED_SECURITY_TAGS###"` +var timeControlConnectedPlugUDev = []string{`SUBSYSTEM=="rtc"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/tpm.go snapd-2.29.3/interfaces/builtin/tpm.go --- snapd-2.28.5/interfaces/builtin/tpm.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/tpm.go 2017-11-09 12:08:52.000000000 +0000 @@ -35,7 +35,7 @@ /dev/tpm0 rw, ` -const tpmConnectedPlugUDev = `KERNEL=="tpm[0-9]*", TAG+="###CONNECTED_SECURITY_TAGS###"` +var tpmConnectedPlugUDev = []string{`KERNEL=="tpm[0-9]*"`} func init() { registerIface(&commonInterface{ diff -Nru snapd-2.28.5/interfaces/builtin/udisks2.go snapd-2.29.3/interfaces/builtin/udisks2.go --- snapd-2.28.5/interfaces/builtin/udisks2.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/udisks2.go 2017-11-09 12:08:52.000000000 +0000 @@ -112,7 +112,7 @@ # Allow connected clients to interact with the service. This gives privileged # access to the system. -dbus (send) +dbus (receive, send) bus=system path=/org/freedesktop/UDisks2/** interface=org.freedesktop.DBus.Properties @@ -353,12 +353,6 @@ ENV{ID_PART_TABLE_TYPE}=="dos", ENV{ID_PART_ENTRY_TYPE}=="0x0", ENV{ID_PART_ENTRY_NUMBER}=="1", ENV{ID_FS_TYPE}=="iso9660|udf", ENV{UDISKS_IGNORE}="0" ` -const udisks2PermanentSlotUDevTag = ` -SUBSYSTEM=="block", TAG+="###CONNECTED_SECURITY_TAGS###" -# This tags all USB devices, so we'll use AppArmor to mediate specific access (eg, /dev/sd* and /dev/mmcblk*) -SUBSYSTEM=="usb", TAG+="###CONNECTED_SECURITY_TAGS###" -` - type udisks2Interface struct{} func (iface *udisks2Interface) Name() string { @@ -396,13 +390,10 @@ } func (iface *udisks2Interface) UDevPermanentSlot(spec *udev.Specification, slot *interfaces.Slot) error { - old := "###CONNECTED_SECURITY_TAGS###" - udevRule := udisks2PermanentSlotUDev - for appName := range slot.Apps { - tag := udevSnapSecurityName(slot.Snap.Name(), appName) - udevRule += strings.Replace(udisks2PermanentSlotUDevTag, old, tag, -1) - } - spec.AddSnippet(udevRule) + spec.AddSnippet(udisks2PermanentSlotUDev) + spec.TagDevice(`SUBSYSTEM=="block"`) + // # This tags all USB devices, so we'll use AppArmor to mediate specific access (eg, /dev/sd* and /dev/mmcblk*) + spec.TagDevice(`SUBSYSTEM=="usb"`) return nil } diff -Nru snapd-2.28.5/interfaces/builtin/udisks2_test.go snapd-2.29.3/interfaces/builtin/udisks2_test.go --- snapd-2.28.5/interfaces/builtin/udisks2_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/udisks2_test.go 2017-11-09 12:08:52.000000000 +0000 @@ -165,9 +165,9 @@ func (s *UDisks2InterfaceSuite) TestUDevSpec(c *C) { spec := &udev.Specification{} c.Assert(spec.AddPermanentSlot(s.iface, s.slot), IsNil) - c.Assert(spec.Snippets(), HasLen, 1) + c.Assert(spec.Snippets(), HasLen, 3) c.Assert(spec.Snippets()[0], testutil.Contains, `LABEL="udisks_probe_end"`) - c.Assert(spec.Snippets()[0], testutil.Contains, `SUBSYSTEM=="usb", TAG+="snap_producer_app"`) + c.Assert(spec.Snippets(), testutil.Contains, `SUBSYSTEM=="usb", TAG+="snap_producer_app"`) } func (s *UDisks2InterfaceSuite) TestSecCompSpec(c *C) { diff -Nru snapd-2.28.5/interfaces/builtin/uhid.go snapd-2.29.3/interfaces/builtin/uhid.go --- snapd-2.28.5/interfaces/builtin/uhid.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/uhid.go 2017-11-03 05:48:53.000000000 +0000 @@ -37,7 +37,7 @@ /dev/uhid rw, ` -const uhidConnectedPlugUDev = `KERNEL=="uhid", TAG+="###CONNECTED_SECURITY_TAGS###"` +// Note: uhid is not represented in sysfs so it cannot be udev tagged func init() { registerIface(&commonInterface{ @@ -47,7 +47,6 @@ implicitOnClassic: true, baseDeclarationSlots: uhidBaseDeclarationSlots, connectedPlugAppArmor: uhidConnectedPlugAppArmor, - connectedPlugUDev: uhidConnectedPlugUDev, reservedForOS: true, }) } diff -Nru snapd-2.28.5/interfaces/builtin/uhid_test.go snapd-2.29.3/interfaces/builtin/uhid_test.go --- snapd-2.28.5/interfaces/builtin/uhid_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/uhid_test.go 2017-11-03 05:48:53.000000000 +0000 @@ -25,7 +25,6 @@ "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/apparmor" "github.com/snapcore/snapd/interfaces/builtin" - "github.com/snapcore/snapd/interfaces/udev" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/testutil" ) @@ -84,13 +83,6 @@ c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "/dev/uhid rw,\n") } -func (s *UhidInterfaceSuite) TestUDevSpec(c *C) { - spec := &udev.Specification{} - c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.slot, nil), IsNil) - c.Assert(spec.Snippets(), HasLen, 1) - c.Assert(spec.Snippets()[0], Equals, `KERNEL=="uhid", TAG+="snap_consumer_app"`) -} - func (s *UhidInterfaceSuite) TestStaticInfo(c *C) { si := interfaces.StaticInfoOf(s.iface) c.Assert(si.ImplicitOnCore, Equals, true) diff -Nru snapd-2.28.5/interfaces/builtin/unity7.go snapd-2.29.3/interfaces/builtin/unity7.go --- snapd-2.28.5/interfaces/builtin/unity7.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/unity7.go 2017-10-30 13:27:31.000000000 +0000 @@ -214,7 +214,10 @@ # subset of freedesktop.org /usr/share/mime/** r, owner @{HOME}/.local/share/mime/** r, -owner @{HOME}/.config/user-dirs.dirs r, +owner @{HOME}/.config/user-dirs.* r, + +/etc/xdg/user-dirs.conf r, +/etc/xdg/user-dirs.defaults r, # gtk settings (subset of gnome abstraction) owner @{HOME}/.config/gtk-2.0/gtkfilechooser.ini r, @@ -543,10 +546,6 @@ path=/org/gnome/SettingsDaemon/MediaKeys member="Get{,All}" peer=(label=unconfined), - -# Lttng tracing is very noisy and should not be allowed by confined apps. Can -# safely deny. LP: #1260491 -deny /{dev,run,var/run}/shm/lttng-ust-* rw, ` const unity7ConnectedPlugSeccomp = ` diff -Nru snapd-2.28.5/interfaces/builtin/unity8.go snapd-2.29.3/interfaces/builtin/unity8.go --- snapd-2.28.5/interfaces/builtin/unity8.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/unity8.go 2017-10-23 06:17:27.000000000 +0000 @@ -86,10 +86,6 @@ path=/ member={PasteboardChanged,PasteFormatsChanged,PasteSelected,PasteSelectionCancelled} peer=(name=com.ubuntu.content.dbus.Service,label=###SLOT_SECURITY_TAGS###), - -# Lttng tracing is very noisy and should not be allowed by confined apps. -# Can safely deny. LP: #1260491 -deny /{dev,run,var/run}/shm/lttng-ust-* rw, ` const unity8ConnectedPlugSecComp = ` diff -Nru snapd-2.28.5/interfaces/builtin/utils_test.go snapd-2.29.3/interfaces/builtin/utils_test.go --- snapd-2.28.5/interfaces/builtin/utils_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/builtin/utils_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,15 +20,12 @@ package builtin_test import ( - "fmt" - . "gopkg.in/check.v1" "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/builtin" "github.com/snapcore/snapd/interfaces/ifacetest" "github.com/snapcore/snapd/snap" - "github.com/snapcore/snapd/snap/snaptest" ) type utilsSuite struct { @@ -67,17 +64,9 @@ } func MockPlug(c *C, yaml string, si *snap.SideInfo, plugName string) *interfaces.Plug { - info := snaptest.MockInfo(c, yaml, si) - if plugInfo, ok := info.Plugs[plugName]; ok { - return &interfaces.Plug{PlugInfo: plugInfo} - } - panic(fmt.Sprintf("cannot find plug %q in snap %q", plugName, info.Name())) + return builtin.MockPlug(c, yaml, si, plugName) } func MockSlot(c *C, yaml string, si *snap.SideInfo, slotName string) *interfaces.Slot { - info := snaptest.MockInfo(c, yaml, si) - if slotInfo, ok := info.Slots[slotName]; ok { - return &interfaces.Slot{SlotInfo: slotInfo} - } - panic(fmt.Sprintf("cannot find slot %q in snap %q", slotName, info.Name())) + return builtin.MockSlot(c, yaml, si, slotName) } diff -Nru snapd-2.28.5/interfaces/core.go snapd-2.29.3/interfaces/core.go --- snapd-2.28.5/interfaces/core.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/core.go 2017-10-23 06:17:27.000000000 +0000 @@ -118,6 +118,15 @@ SlotRef SlotRef } +type Connection struct { + plugInfo *snap.PlugInfo + slotInfo *snap.SlotInfo +} + +func (conn *Connection) Interface() string { + return conn.plugInfo.Interface +} + // ID returns a string identifying a given connection. func (conn *ConnRef) ID() string { return fmt.Sprintf("%s:%s %s:%s", conn.PlugRef.Snap, conn.PlugRef.Name, conn.SlotRef.Snap, conn.SlotRef.Name) diff -Nru snapd-2.28.5/interfaces/mount/backend.go snapd-2.29.3/interfaces/mount/backend.go --- snapd-2.28.5/interfaces/mount/backend.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/interfaces/mount/backend.go 2017-10-23 06:17:27.000000000 +0000 @@ -98,17 +98,8 @@ } fstate := &osutil.FileState{Content: buffer.Bytes(), Mode: 0644} content := make(map[string]*osutil.FileState) - // Add the new per-snap fstab file. This file will be read by snap-confine. + // Add the per-snap fstab file. This file is read by snap-update-ns. content[fmt.Sprintf("snap.%s.fstab", snapInfo.Name())] = fstate - // Add legacy per-app/per-hook fstab files. Those are identical but - // snap-confine doesn't yet load it from a per-snap location. This can be - // safely removed once snap-confine is updated. - for _, appInfo := range snapInfo.Apps { - content[fmt.Sprintf("%s.fstab", appInfo.SecurityTag())] = fstate - } - for _, hookInfo := range snapInfo.Hooks { - content[fmt.Sprintf("%s.fstab", hookInfo.SecurityTag())] = fstate - } return content } diff -Nru snapd-2.28.5/interfaces/mount/backend_test.go snapd-2.29.3/interfaces/mount/backend_test.go --- snapd-2.28.5/interfaces/mount/backend_test.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/interfaces/mount/backend_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -150,15 +150,6 @@ got := strings.Split(string(content), "\n") sort.Strings(got) c.Check(got, DeepEquals, expected) - // and that we have the legacy, per app/hook files as well. - for _, binary := range []string{"app1", "app2", "hook.configure"} { - fn := filepath.Join(dirs.SnapMountPolicyDir, fmt.Sprintf("snap.snap-name.%s.fstab", binary)) - content, err := ioutil.ReadFile(fn) - c.Assert(err, IsNil, Commentf("Expected mount profile for %q", binary)) - got := strings.Split(string(content), "\n") - sort.Strings(got) - c.Check(got, DeepEquals, expected) - } } func (s *backendSuite) TestSetupSetsupWithoutDir(c *C) { @@ -169,9 +160,4 @@ // Ensure that backend.Setup() creates the required dir on demand os.Remove(dirs.SnapMountPolicyDir) s.InstallSnap(c, interfaces.ConfinementOptions{}, mockSnapYaml, 0) - - for _, binary := range []string{"app1", "app2", "hook.configure"} { - fn := filepath.Join(dirs.SnapMountPolicyDir, fmt.Sprintf("snap.snap-name.%s.fstab", binary)) - c.Assert(osutil.FileExists(fn), Equals, true, Commentf("Expected mount file for %q", binary)) - } } diff -Nru snapd-2.28.5/interfaces/mount/change.go snapd-2.29.3/interfaces/mount/change.go --- snapd-2.28.5/interfaces/mount/change.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/interfaces/mount/change.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,145 +0,0 @@ -// -*- Mode: Go; indent-tabs-mode: t -*- - -/* - * Copyright (C) 2017 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 . - * - */ - -package mount - -import ( - "fmt" - "path" - "sort" - "strings" - "syscall" -) - -// Action represents a mount action (mount, remount, unmount, etc). -type Action string - -const ( - // Keep indicates that a given mount entry should be kept as-is. - Keep Action = "keep" - // Mount represents an action that results in mounting something somewhere. - Mount Action = "mount" - // Unmount represents an action that results in unmounting something from somewhere. - Unmount Action = "unmount" - // Remount when needed -) - -// Change describes a change to the mount table (action and the entry to act on). -type Change struct { - Entry Entry - Action Action -} - -// String formats mount change to a human-readable line. -func (c Change) String() string { - return fmt.Sprintf("%s (%s)", c.Action, c.Entry) -} - -// Perform executes the desired mount or unmount change using system calls. -// Filesystems that depend on helper programs or multiple independent calls to -// the kernel (--make-shared, for example) are unsupported. -func (c *Change) Perform() error { - switch c.Action { - case Mount: - flags, err := OptsToFlags(c.Entry.Options) - if err != nil { - return err - } - return syscall.Mount(c.Entry.Name, c.Entry.Dir, c.Entry.Type, uintptr(flags), "") - case Unmount: - const UMOUNT_NOFOLLOW = 8 - return syscall.Unmount(c.Entry.Dir, UMOUNT_NOFOLLOW) - } - return fmt.Errorf("cannot process mount change, unknown action: %q", c.Action) -} - -// NeededChanges computes the changes required to change current to desired mount entries. -// -// The current and desired profiles is a fstab like list of mount entries. The -// lists are processed and a "diff" of mount changes is produced. The mount -// changes, when applied in order, transform the current profile into the -// desired profile. -func NeededChanges(currentProfile, desiredProfile *Profile) []Change { - // Copy both profiles as we will want to mutate them. - current := make([]Entry, len(currentProfile.Entries)) - copy(current, currentProfile.Entries) - desired := make([]Entry, len(desiredProfile.Entries)) - copy(desired, desiredProfile.Entries) - - // Clean the directory part of both profiles. This is done so that we can - // easily test if a given directory is a subdirectory with - // strings.HasPrefix coupled with an extra slash character. - for i := range current { - current[i].Dir = path.Clean(current[i].Dir) - } - for i := range desired { - desired[i].Dir = path.Clean(desired[i].Dir) - } - - // Sort both lists by directory name with implicit trailing slash. - sort.Sort(byMagicDir(current)) - sort.Sort(byMagicDir(desired)) - - // Construct a desired directory map. - desiredMap := make(map[string]*Entry) - for i := range desired { - desiredMap[desired[i].Dir] = &desired[i] - } - - // Compute reusable entries: those which are equal in current and desired and which - // are not prefixed by another entry that changed. - var reuse map[string]bool - var skipDir string - for i := range current { - dir := current[i].Dir - if skipDir != "" && strings.HasPrefix(dir, skipDir) { - continue - } - skipDir = "" // reset skip prefix as it no longer applies - if entry, ok := desiredMap[dir]; ok && current[i].Equal(entry) { - if reuse == nil { - reuse = make(map[string]bool) - } - reuse[dir] = true - continue - } - skipDir = strings.TrimSuffix(dir, "/") + "/" - } - - // We are now ready to compute the necessary mount changes. - var changes []Change - - // Unmount entries not reused in reverse to handle children before their parent. - for i := len(current) - 1; i >= 0; i-- { - if reuse[current[i].Dir] { - changes = append(changes, Change{Action: Keep, Entry: current[i]}) - } else { - changes = append(changes, Change{Action: Unmount, Entry: current[i]}) - } - } - - // Mount desired entries not reused. - for i := range desired { - if !reuse[desired[i].Dir] { - changes = append(changes, Change{Action: Mount, Entry: desired[i]}) - } - } - - return changes -} diff -Nru snapd-2.28.5/interfaces/mount/change_test.go snapd-2.29.3/interfaces/mount/change_test.go --- snapd-2.28.5/interfaces/mount/change_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/interfaces/mount/change_test.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,176 +0,0 @@ -// -*- Mode: Go; indent-tabs-mode: t -*- - -/* - * Copyright (C) 2017 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 . - * - */ - -package mount_test - -import ( - . "gopkg.in/check.v1" - - "github.com/snapcore/snapd/interfaces/mount" -) - -type changeSuite struct{} - -var _ = Suite(&changeSuite{}) - -func (s *changeSuite) TestString(c *C) { - change := mount.Change{ - Entry: mount.Entry{Dir: "/a/b", Name: "/dev/sda1"}, - Action: mount.Mount, - } - c.Assert(change.String(), Equals, "mount (/dev/sda1 /a/b none defaults 0 0)") -} - -// When there are no profiles we don't do anything. -func (s *changeSuite) TestNeededChangesNoProfiles(c *C) { - current := &mount.Profile{} - desired := &mount.Profile{} - changes := mount.NeededChanges(current, desired) - c.Assert(changes, IsNil) -} - -// When the profiles are the same we don't do anything. -func (s *changeSuite) TestNeededChangesNoChange(c *C) { - current := &mount.Profile{Entries: []mount.Entry{{Dir: "/common/stuf"}}} - desired := &mount.Profile{Entries: []mount.Entry{{Dir: "/common/stuf"}}} - changes := mount.NeededChanges(current, desired) - c.Assert(changes, DeepEquals, []mount.Change{ - {Entry: mount.Entry{Dir: "/common/stuf"}, Action: mount.Keep}, - }) -} - -// When the content interface is connected we should mount the new entry. -func (s *changeSuite) TestNeededChangesTrivialMount(c *C) { - current := &mount.Profile{} - desired := &mount.Profile{Entries: []mount.Entry{{Dir: "/common/stuf"}}} - changes := mount.NeededChanges(current, desired) - c.Assert(changes, DeepEquals, []mount.Change{ - {Entry: desired.Entries[0], Action: mount.Mount}, - }) -} - -// When the content interface is disconnected we should unmount the mounted entry. -func (s *changeSuite) TestNeededChangesTrivialUnmount(c *C) { - current := &mount.Profile{Entries: []mount.Entry{{Dir: "/common/stuf"}}} - desired := &mount.Profile{} - changes := mount.NeededChanges(current, desired) - c.Assert(changes, DeepEquals, []mount.Change{ - {Entry: current.Entries[0], Action: mount.Unmount}, - }) -} - -// When umounting we unmount children before parents. -func (s *changeSuite) TestNeededChangesUnmountOrder(c *C) { - current := &mount.Profile{Entries: []mount.Entry{ - {Dir: "/common/stuf/extra"}, - {Dir: "/common/stuf"}, - }} - desired := &mount.Profile{} - changes := mount.NeededChanges(current, desired) - c.Assert(changes, DeepEquals, []mount.Change{ - {Entry: mount.Entry{Dir: "/common/stuf/extra"}, Action: mount.Unmount}, - {Entry: mount.Entry{Dir: "/common/stuf"}, Action: mount.Unmount}, - }) -} - -// When mounting we mount the parents before the children. -func (s *changeSuite) TestNeededChangesMountOrder(c *C) { - current := &mount.Profile{} - desired := &mount.Profile{Entries: []mount.Entry{ - {Dir: "/common/stuf/extra"}, - {Dir: "/common/stuf"}, - }} - changes := mount.NeededChanges(current, desired) - c.Assert(changes, DeepEquals, []mount.Change{ - {Entry: mount.Entry{Dir: "/common/stuf"}, Action: mount.Mount}, - {Entry: mount.Entry{Dir: "/common/stuf/extra"}, Action: mount.Mount}, - }) -} - -// When parent changes we don't reuse its children -func (s *changeSuite) TestNeededChangesChangedParentSameChild(c *C) { - current := &mount.Profile{Entries: []mount.Entry{ - {Dir: "/common/stuf", Name: "/dev/sda1"}, - {Dir: "/common/stuf/extra"}, - {Dir: "/common/unrelated"}, - }} - desired := &mount.Profile{Entries: []mount.Entry{ - {Dir: "/common/stuf", Name: "/dev/sda2"}, - {Dir: "/common/stuf/extra"}, - {Dir: "/common/unrelated"}, - }} - changes := mount.NeededChanges(current, desired) - c.Assert(changes, DeepEquals, []mount.Change{ - {Entry: mount.Entry{Dir: "/common/unrelated"}, Action: mount.Keep}, - {Entry: mount.Entry{Dir: "/common/stuf/extra"}, Action: mount.Unmount}, - {Entry: mount.Entry{Dir: "/common/stuf", Name: "/dev/sda1"}, Action: mount.Unmount}, - {Entry: mount.Entry{Dir: "/common/stuf", Name: "/dev/sda2"}, Action: mount.Mount}, - {Entry: mount.Entry{Dir: "/common/stuf/extra"}, Action: mount.Mount}, - }) -} - -// When child changes we don't touch the unchanged parent -func (s *changeSuite) TestNeededChangesSameParentChangedChild(c *C) { - current := &mount.Profile{Entries: []mount.Entry{ - {Dir: "/common/stuf"}, - {Dir: "/common/stuf/extra", Name: "/dev/sda1"}, - {Dir: "/common/unrelated"}, - }} - desired := &mount.Profile{Entries: []mount.Entry{ - {Dir: "/common/stuf"}, - {Dir: "/common/stuf/extra", Name: "/dev/sda2"}, - {Dir: "/common/unrelated"}, - }} - changes := mount.NeededChanges(current, desired) - c.Assert(changes, DeepEquals, []mount.Change{ - {Entry: mount.Entry{Dir: "/common/unrelated"}, Action: mount.Keep}, - {Entry: mount.Entry{Dir: "/common/stuf/extra", Name: "/dev/sda1"}, Action: mount.Unmount}, - {Entry: mount.Entry{Dir: "/common/stuf"}, Action: mount.Keep}, - {Entry: mount.Entry{Dir: "/common/stuf/extra", Name: "/dev/sda2"}, Action: mount.Mount}, - }) -} - -// cur = ['/a/b', '/a/b-1', '/a/b-1/3', '/a/b/c'] -// des = ['/a/b', '/a/b-1', '/a/b/c' -// -// We are smart about comparing entries as directories. Here even though "/a/b" -// is a prefix of "/a/b-1" it is correctly reused. -func (s *changeSuite) TestNeededChangesSmartEntryComparison(c *C) { - current := &mount.Profile{Entries: []mount.Entry{ - {Dir: "/a/b", Name: "/dev/sda1"}, - {Dir: "/a/b-1"}, - {Dir: "/a/b-1/3"}, - {Dir: "/a/b/c"}, - }} - desired := &mount.Profile{Entries: []mount.Entry{ - {Dir: "/a/b", Name: "/dev/sda2"}, - {Dir: "/a/b-1"}, - {Dir: "/a/b/c"}, - }} - changes := mount.NeededChanges(current, desired) - c.Assert(changes, DeepEquals, []mount.Change{ - {Entry: mount.Entry{Dir: "/a/b/c"}, Action: mount.Unmount}, - {Entry: mount.Entry{Dir: "/a/b", Name: "/dev/sda1"}, Action: mount.Unmount}, - {Entry: mount.Entry{Dir: "/a/b-1/3"}, Action: mount.Unmount}, - {Entry: mount.Entry{Dir: "/a/b-1"}, Action: mount.Keep}, - - {Entry: mount.Entry{Dir: "/a/b", Name: "/dev/sda2"}, Action: mount.Mount}, - {Entry: mount.Entry{Dir: "/a/b/c"}, Action: mount.Mount}, - }) -} diff -Nru snapd-2.28.5/interfaces/mount/sorting.go snapd-2.29.3/interfaces/mount/sorting.go --- snapd-2.28.5/interfaces/mount/sorting.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/interfaces/mount/sorting.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -// -*- Mode: Go; indent-tabs-mode: t -*- - -/* - * Copyright (C) 2017 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 . - * - */ - -package mount - -import ( - "strings" -) - -// byMagicDir allows sorting an array of entries that automagically assumes -// each entry ends with a trailing slash. -type byMagicDir []Entry - -func (c byMagicDir) Len() int { return len(c) } -func (c byMagicDir) Swap(i, j int) { c[i], c[j] = c[j], c[i] } -func (c byMagicDir) Less(i, j int) bool { - iDir := c[i].Dir - jDir := c[j].Dir - if !strings.HasSuffix(iDir, "/") { - iDir = iDir + "/" - } - if !strings.HasSuffix(jDir, "/") { - jDir = jDir + "/" - } - return iDir < jDir -} diff -Nru snapd-2.28.5/interfaces/mount/sorting_test.go snapd-2.29.3/interfaces/mount/sorting_test.go --- snapd-2.28.5/interfaces/mount/sorting_test.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/interfaces/mount/sorting_test.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -// -*- Mode: Go; indent-tabs-mode: t -*- - -/* - * Copyright (C) 2017 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 . - * - */ - -package mount - -import ( - "sort" - - . "gopkg.in/check.v1" -) - -type sortSuite struct{} - -var _ = Suite(&sortSuite{}) - -func (s *sortSuite) TestTrailingSlashesComparison(c *C) { - // Naively sorted entries. - entries := []Entry{ - {Dir: "/a/b"}, - {Dir: "/a/b-1"}, - {Dir: "/a/b-1/3"}, - {Dir: "/a/b/c"}, - } - sort.Sort(byMagicDir(entries)) - // Entries sorted as if they had a trailing slash. - c.Assert(entries, DeepEquals, []Entry{ - {Dir: "/a/b-1"}, - {Dir: "/a/b-1/3"}, - {Dir: "/a/b"}, - {Dir: "/a/b/c"}, - }) -} diff -Nru snapd-2.28.5/interfaces/repo.go snapd-2.29.3/interfaces/repo.go --- snapd-2.28.5/interfaces/repo.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/repo.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,7 +20,6 @@ package interfaces import ( - "bytes" "fmt" "sort" "strings" @@ -38,22 +37,24 @@ plugs map[string]map[string]*Plug slots map[string]map[string]*Slot // given a slot and a plug, are they connected? - slotPlugs map[*Slot]map[*Plug]bool + slotPlugs map[*Slot]map[*Plug]*Connection // given a plug and a slot, are they connected? - plugSlots map[*Plug]map[*Slot]bool + plugSlots map[*Plug]map[*Slot]*Connection backends map[SecuritySystem]SecurityBackend } // NewRepository creates an empty plug repository. func NewRepository() *Repository { - return &Repository{ + repo := &Repository{ ifaces: make(map[string]Interface), plugs: make(map[string]map[string]*Plug), slots: make(map[string]map[string]*Slot), - slotPlugs: make(map[*Slot]map[*Plug]bool), - plugSlots: make(map[*Plug]map[*Slot]bool), + slotPlugs: make(map[*Slot]map[*Plug]*Connection), + plugSlots: make(map[*Plug]map[*Slot]*Connection), backends: make(map[SecuritySystem]SecurityBackend), } + + return repo } // Interface returns an interface with a given name. @@ -146,15 +147,15 @@ if opts != nil && opts.Connected { connected = make(map[string]bool) for _, plugMap := range r.slotPlugs { - for plug, ok := range plugMap { - if ok { + for plug, conn := range plugMap { + if conn != nil { connected[plug.Interface] = true } } } for _, slotMap := range r.plugSlots { - for slot, ok := range slotMap { - if ok { + for slot, conn := range slotMap { + if conn != nil { connected[slot.Interface] = true } } @@ -519,7 +520,7 @@ return nil, fmt.Errorf("snap %q has no slot named %q", slotSnapName, slotName) } // Ensure that slot and plug are connected - if !r.slotPlugs[slot][plug] { + if r.slotPlugs[slot][plug] == nil { return nil, fmt.Errorf("cannot disconnect %s:%s from %s:%s, it is not connected", plugSnapName, plugName, slotSnapName, slotName) } @@ -572,19 +573,20 @@ plugSnapName, plugName, plug.Interface, slotSnapName, slotName, slot.Interface) } // Ensure that slot and plug are not connected yet - if r.slotPlugs[slot][plug] { + if r.slotPlugs[slot][plug] != nil { // But if they are don't treat this as an error. return nil } // Connect the plug if r.slotPlugs[slot] == nil { - r.slotPlugs[slot] = make(map[*Plug]bool) + r.slotPlugs[slot] = make(map[*Plug]*Connection) } if r.plugSlots[plug] == nil { - r.plugSlots[plug] = make(map[*Slot]bool) + r.plugSlots[plug] = make(map[*Slot]*Connection) } - r.slotPlugs[slot][plug] = true - r.plugSlots[plug][slot] = true + conn := &Connection{plugInfo: plug.PlugInfo, slotInfo: slot.SlotInfo} + r.slotPlugs[slot][plug] = conn + r.plugSlots[plug][slot] = conn slot.Connections = append(slot.Connections, PlugRef{plug.Snap.Name(), plug.Name}) plug.Connections = append(plug.Connections, SlotRef{slot.Snap.Name(), slot.Name}) return nil @@ -624,7 +626,7 @@ return fmt.Errorf("snap %q has no slot named %q", slotSnapName, slotName) } // Ensure that slot and plug are connected - if !r.slotPlugs[slot][plug] { + if r.slotPlugs[slot][plug] == nil { return fmt.Errorf("cannot disconnect %s:%s from %s:%s, it is not connected", plugSnapName, plugName, slotSnapName, slotName) } @@ -814,39 +816,6 @@ return spec, nil } -// BadInterfacesError is returned when some snap interfaces could not be registered. -// Those interfaces not mentioned in the error were successfully registered. -type BadInterfacesError struct { - snap string - issues map[string]string // slot or plug name => message -} - -func (e *BadInterfacesError) Error() string { - inverted := make(map[string][]string) - for name, reason := range e.issues { - inverted[reason] = append(inverted[reason], name) - } - var buf bytes.Buffer - fmt.Fprintf(&buf, "snap %q has bad plugs or slots: ", e.snap) - reasons := make([]string, 0, len(inverted)) - for reason := range inverted { - reasons = append(reasons, reason) - } - sort.Strings(reasons) - for _, reason := range reasons { - names := inverted[reason] - sort.Strings(names) - for i, name := range names { - if i > 0 { - buf.WriteString(", ") - } - buf.WriteString(name) - } - fmt.Fprintf(&buf, " (%s); ", reason) - } - return strings.TrimSuffix(buf.String(), "; ") -} - // AddSnap adds plugs and slots declared by the given snap to the repository. // // This function can be used to implement snap install or, when used along with @@ -875,58 +844,21 @@ return fmt.Errorf("cannot register interfaces for snap %q more than once", snapName) } - bad := BadInterfacesError{ - snap: snapName, - issues: make(map[string]string), - } - for plugName, plugInfo := range snapInfo.Plugs { - iface, ok := r.ifaces[plugInfo.Interface] - if !ok { - bad.issues[plugName] = "unknown interface" - continue - } - // Reject plug with invalid name - if err := ValidateName(plugName); err != nil { - bad.issues[plugName] = err.Error() - continue - } - plug := &Plug{PlugInfo: plugInfo} - if err := plug.Sanitize(iface); err != nil { - bad.issues[plugName] = err.Error() - continue - } if r.plugs[snapName] == nil { r.plugs[snapName] = make(map[string]*Plug) } + plug := &Plug{PlugInfo: plugInfo} r.plugs[snapName][plugName] = plug } for slotName, slotInfo := range snapInfo.Slots { - iface, ok := r.ifaces[slotInfo.Interface] - if !ok { - bad.issues[slotName] = "unknown interface" - continue - } - // Reject slot with invalid name - if err := ValidateName(slotName); err != nil { - bad.issues[slotName] = err.Error() - continue - } - slot := &Slot{SlotInfo: slotInfo} - if err := slot.Sanitize(iface); err != nil { - bad.issues[slotName] = err.Error() - continue - } if r.slots[snapName] == nil { r.slots[snapName] = make(map[string]*Slot) } + slot := &Slot{SlotInfo: slotInfo} r.slots[snapName][slotName] = slot } - - if len(bad.issues) > 0 { - return &bad - } return nil } diff -Nru snapd-2.28.5/interfaces/repo_test.go snapd-2.29.3/interfaces/repo_test.go --- snapd-2.28.5/interfaces/repo_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/interfaces/repo_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -1539,38 +1539,6 @@ c.Assert(err, IsNil) } -func (s *AddRemoveSuite) TestAddSnapComplexErrorHandling(c *C) { - err := s.repo.AddInterface(&ifacetest.TestInterface{ - InterfaceName: "invalid-plug-iface", - SanitizePlugCallback: func(plug *Plug) error { return fmt.Errorf("plug is invalid") }, - SanitizeSlotCallback: func(slot *Slot) error { return fmt.Errorf("slot is invalid") }, - }) - c.Assert(err, IsNil) - err = s.repo.AddInterface(&ifacetest.TestInterface{ - InterfaceName: "invalid-slot-iface", - SanitizePlugCallback: func(plug *Plug) error { return fmt.Errorf("plug is invalid") }, - SanitizeSlotCallback: func(slot *Slot) error { return fmt.Errorf("slot is invalid") }, - }) - c.Assert(err, IsNil) - snapInfo := snaptest.MockInfo(c, ` -name: complex -plugs: - invalid-plug-iface: - unknown-plug-iface: -slots: - invalid-slot-iface: - unknown-slot-iface: -`, nil) - err = s.repo.AddSnap(snapInfo) - c.Check(err, ErrorMatches, - `snap "complex" has bad plugs or slots: invalid-plug-iface \(plug is invalid\); invalid-slot-iface \(slot is invalid\); unknown-plug-iface, unknown-slot-iface \(unknown interface\)`) - // Nothing was added - c.Check(s.repo.Plug("complex", "invalid-plug-iface"), IsNil) - c.Check(s.repo.Plug("complex", "unknown-plug-iface"), IsNil) - c.Check(s.repo.Slot("complex", "invalid-slot-iface"), IsNil) - c.Check(s.repo.Slot("complex", "unknown-slot-iface"), IsNil) -} - const testConsumerYaml = ` name: consumer apps: @@ -1616,18 +1584,6 @@ c.Assert(s.repo.Plug("consumer", "iface"), Not(IsNil)) } -func (s *AddRemoveSuite) TestAddSnapErrorsOnInvalidSlotNames(c *C) { - _, err := s.addSnap(c, testConsumerInvalidSlotNameYaml) - c.Assert(err, NotNil) - c.Check(err, ErrorMatches, `snap "consumer" has bad plugs or slots: ttyS5 \(invalid interface name: "ttyS5"\)`) -} - -func (s *AddRemoveSuite) TestAddSnapErrorsOnInvalidPlugNames(c *C) { - _, err := s.addSnap(c, testConsumerInvalidPlugNameYaml) - c.Assert(err, NotNil) - c.Check(err, ErrorMatches, `snap "consumer" has bad plugs or slots: ttyS3 \(invalid interface name: "ttyS3"\)`) -} - func (s *AddRemoveSuite) TestAddSnapErrorsOnExistingSnapPlugs(c *C) { _, err := s.addSnap(c, testConsumerYaml) c.Assert(err, IsNil) diff -Nru snapd-2.28.5/interfaces/systemd/backend_test.go snapd-2.29.3/interfaces/systemd/backend_test.go --- snapd-2.28.5/interfaces/systemd/backend_test.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/interfaces/systemd/backend_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -29,14 +29,15 @@ "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/ifacetest" "github.com/snapcore/snapd/interfaces/systemd" - "github.com/snapcore/snapd/testutil" sysd "github.com/snapcore/snapd/systemd" ) type backendSuite struct { ifacetest.BackendSuite - systemctlCmd *testutil.MockCmd + + systemctlArgs [][]string + systemctlRestorer func() } var _ = Suite(&backendSuite{}) @@ -52,11 +53,14 @@ s.Backend = &systemd.Backend{} s.BackendSuite.SetUpTest(c) c.Assert(s.Repo.AddBackend(s.Backend), IsNil) - s.systemctlCmd = testutil.MockCommand(c, "systemctl", "echo ActiveState=inactive") + s.systemctlRestorer = sysd.MockSystemctl(func(args ...string) ([]byte, error) { + s.systemctlArgs = append(s.systemctlArgs, append([]string{"systemctl"}, args...)) + return []byte("ActiveState=inactive"), nil + }) } func (s *backendSuite) TearDownTest(c *C) { - s.systemctlCmd.Restore() + s.systemctlRestorer() s.BackendSuite.TearDownTest(c) } @@ -65,17 +69,17 @@ } func (s *backendSuite) TestInstallingSnapWritesStartsServices(c *C) { - prevctlCmd := sysd.SystemctlCmd - defer func() { sysd.SystemctlCmd = prevctlCmd }() - var sysdLog [][]string - sysd.SystemctlCmd = func(cmd ...string) ([]byte, error) { + + r := sysd.MockSystemctl(func(cmd ...string) ([]byte, error) { sysdLog = append(sysdLog, cmd) if cmd[0] == "show" { return []byte("ActiveState=inactive\n"), nil } return []byte{}, nil - } + }) + defer r() + s.Iface.SystemdPermanentSlotCallback = func(spec *systemd.Specification, slot *interfaces.Slot) error { return spec.AddService("snap.samba.interface.foo.service", &systemd.Service{ExecStart: "/bin/true"}) } @@ -100,14 +104,14 @@ } for _, opts := range testedConfinementOpts { snapInfo := s.InstallSnap(c, opts, ifacetest.SambaYamlV1, 1) - s.systemctlCmd.ForgetCalls() + s.systemctlArgs = nil s.RemoveSnap(c, snapInfo) service := filepath.Join(dirs.SnapServicesDir, "snap.samba.interface.foo.service") // the service file was removed _, err := os.Stat(service) c.Check(os.IsNotExist(err), Equals, true) // the service was stopped - c.Check(s.systemctlCmd.Calls(), DeepEquals, [][]string{ + c.Check(s.systemctlArgs, DeepEquals, [][]string{ {"systemctl", "--root", dirs.GlobalRootDir, "disable", "snap.samba.interface.foo.service"}, {"systemctl", "stop", "snap.samba.interface.foo.service"}, {"systemctl", "show", "--property=ActiveState", "snap.samba.interface.foo.service"}, @@ -125,7 +129,7 @@ return spec.AddService("snap.samba.interface.bar.service", &systemd.Service{ExecStart: "/bin/false"}) } snapInfo := s.InstallSnap(c, interfaces.ConfinementOptions{}, ifacetest.SambaYamlV1, 1) - s.systemctlCmd.ForgetCalls() + s.systemctlArgs = nil serviceFoo := filepath.Join(dirs.SnapServicesDir, "snap.samba.interface.foo.service") serviceBar := filepath.Join(dirs.SnapServicesDir, "snap.samba.interface.bar.service") // the services were created @@ -141,7 +145,7 @@ // Update over to the same snap to regenerate security s.UpdateSnap(c, snapInfo, interfaces.ConfinementOptions{}, ifacetest.SambaYamlV1, 0) // The bar service should have been stopped - c.Check(s.systemctlCmd.Calls(), DeepEquals, [][]string{ + c.Check(s.systemctlArgs, DeepEquals, [][]string{ {"systemctl", "--root", dirs.GlobalRootDir, "disable", "snap.samba.interface.bar.service"}, {"systemctl", "stop", "snap.samba.interface.bar.service"}, {"systemctl", "show", "--property=ActiveState", "snap.samba.interface.bar.service"}, diff -Nru snapd-2.28.5/interfaces/udev/backend.go snapd-2.29.3/interfaces/udev/backend.go --- snapd-2.28.5/interfaces/udev/backend.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/interfaces/udev/backend.go 2017-11-02 19:49:21.000000000 +0000 @@ -1,7 +1,7 @@ // -*- Mode: Go; indent-tabs-mode: t -*- /* - * Copyright (C) 2016 Canonical Ltd + * Copyright (C) 2016-2017 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 @@ -17,8 +17,8 @@ * */ -// Package udev implements integration between snappy, udev and -// ubuntu-core-laucher around tagging character and block devices so that they +// Package udev implements integration between snapd, udev and +// snap-confine around tagging character and block devices so that they // can be accessed by applications. // // TODO: Document this better @@ -29,6 +29,7 @@ "fmt" "os" "path/filepath" + "strings" "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/interfaces" @@ -85,7 +86,14 @@ var buffer bytes.Buffer buffer.WriteString("# This file is automatically generated.\n") + if (opts.DevMode || opts.Classic) && !opts.JailMode { + buffer.WriteString("# udev tagging/device cgroups disabled with non-strict mode snaps\n") + } for _, snippet := range content { + if (opts.DevMode || opts.Classic) && !opts.JailMode { + buffer.WriteRune('#') + snippet = strings.Replace(snippet, "\n", "\n#", -1) + } buffer.WriteString(snippet) buffer.WriteByte('\n') } diff -Nru snapd-2.28.5/interfaces/udev/backend_test.go snapd-2.29.3/interfaces/udev/backend_test.go --- snapd-2.28.5/interfaces/udev/backend_test.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/interfaces/udev/backend_test.go 2017-11-02 19:49:21.000000000 +0000 @@ -285,7 +285,11 @@ fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") data, err := ioutil.ReadFile(fname) c.Assert(err, IsNil) - c.Check(string(data), Equals, "# This file is automatically generated.\ndummy\n") + if opts.DevMode || opts.Classic { + c.Check(string(data), Equals, "# This file is automatically generated.\n# udev tagging/device cgroups disabled with non-strict mode snaps\n#dummy\n") + } else { + c.Check(string(data), Equals, "# This file is automatically generated.\ndummy\n") + } stat, err := os.Stat(fname) c.Assert(err, IsNil) c.Check(stat.Mode(), Equals, os.FileMode(0644)) @@ -293,6 +297,28 @@ } } +func (s *backendSuite) TestCombineSnippetsWithActualSnippetsWithNewline(c *C) { + // NOTE: Hand out a permanent snippet so that .rules file is generated. + s.Iface.UDevPermanentSlotCallback = func(spec *udev.Specification, slot *interfaces.Slot) error { + spec.AddSnippet("dummy1\ndummy2") + return nil + } + for _, opts := range testedConfinementOpts { + snapInfo := s.InstallSnap(c, opts, ifacetest.SambaYamlV1, 0) + fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.samba.rules") + data, err := ioutil.ReadFile(fname) + c.Assert(err, IsNil) + if opts.DevMode || opts.Classic { + c.Check(string(data), Equals, "# This file is automatically generated.\n# udev tagging/device cgroups disabled with non-strict mode snaps\n#dummy1\n#dummy2\n") + } else { + c.Check(string(data), Equals, "# This file is automatically generated.\ndummy1\ndummy2\n") + } + stat, err := os.Stat(fname) + c.Assert(err, IsNil) + c.Check(stat.Mode(), Equals, os.FileMode(0644)) + s.RemoveSnap(c, snapInfo) + } +} func (s *backendSuite) TestCombineSnippetsWithActualSnippetsWhenPlugNoApps(c *C) { // NOTE: Hand out a permanent snippet so that .rules file is generated. s.Iface.UDevPermanentPlugCallback = func(spec *udev.Specification, slot *interfaces.Plug) error { @@ -304,7 +330,11 @@ fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.foo.rules") data, err := ioutil.ReadFile(fname) c.Assert(err, IsNil) - c.Check(string(data), Equals, "# This file is automatically generated.\ndummy\n") + if opts.DevMode || opts.Classic { + c.Check(string(data), Equals, "# This file is automatically generated.\n# udev tagging/device cgroups disabled with non-strict mode snaps\n#dummy\n") + } else { + c.Check(string(data), Equals, "# This file is automatically generated.\ndummy\n") + } stat, err := os.Stat(fname) c.Assert(err, IsNil) c.Check(stat.Mode(), Equals, os.FileMode(0644)) @@ -323,7 +353,11 @@ fname := filepath.Join(dirs.SnapUdevRulesDir, "70-snap.foo.rules") data, err := ioutil.ReadFile(fname) c.Assert(err, IsNil) - c.Check(string(data), Equals, "# This file is automatically generated.\ndummy\n") + if opts.DevMode || opts.Classic { + c.Check(string(data), Equals, "# This file is automatically generated.\n# udev tagging/device cgroups disabled with non-strict mode snaps\n#dummy\n") + } else { + c.Check(string(data), Equals, "# This file is automatically generated.\ndummy\n") + } stat, err := os.Stat(fname) c.Assert(err, IsNil) c.Check(stat.Mode(), Equals, os.FileMode(0644)) diff -Nru snapd-2.28.5/interfaces/udev/spec.go snapd-2.29.3/interfaces/udev/spec.go --- snapd-2.28.5/interfaces/udev/spec.go 2017-08-18 13:48:10.000000000 +0000 +++ snapd-2.29.3/interfaces/udev/spec.go 2017-11-09 12:08:52.000000000 +0000 @@ -20,7 +20,9 @@ package udev import ( + "fmt" "sort" + "strings" "github.com/snapcore/snapd/interfaces" ) @@ -28,7 +30,8 @@ // Specification assists in collecting udev snippets associated with an interface. type Specification struct { // Snippets are stored in a map for de-duplication - snippets map[string]bool + snippets map[string]bool + securityTags []string } // AddSnippet adds a new udev snippet. @@ -39,6 +42,17 @@ spec.snippets[snippet] = true } +func udevTag(securityTag string) string { + return strings.Replace(securityTag, ".", "_", -1) +} + +// TagDevice adds an app/hook specific udev tag to devices described by the snippet. +func (spec *Specification) TagDevice(snippet string) { + for _, securityTag := range spec.securityTags { + spec.AddSnippet(fmt.Sprintf(`%s, TAG+="%s"`, snippet, udevTag(securityTag))) + } +} + // Snippets returns a copy of all the snippets added so far. func (spec *Specification) Snippets() (result []string) { for k := range spec.snippets { @@ -56,6 +70,8 @@ UDevConnectedPlug(spec *Specification, plug *interfaces.Plug, plugAttrs map[string]interface{}, slot *interfaces.Slot, slotAttrs map[string]interface{}) error } if iface, ok := iface.(definer); ok { + spec.securityTags = plug.SecurityTags() + defer func() { spec.securityTags = nil }() return iface.UDevConnectedPlug(spec, plug, plugAttrs, slot, slotAttrs) } return nil @@ -67,6 +83,8 @@ UDevConnectedSlot(spec *Specification, plug *interfaces.Plug, plugAttrs map[string]interface{}, slot *interfaces.Slot, slotAttrs map[string]interface{}) error } if iface, ok := iface.(definer); ok { + spec.securityTags = slot.SecurityTags() + defer func() { spec.securityTags = nil }() return iface.UDevConnectedSlot(spec, plug, plugAttrs, slot, slotAttrs) } return nil @@ -78,6 +96,8 @@ UDevPermanentPlug(spec *Specification, plug *interfaces.Plug) error } if iface, ok := iface.(definer); ok { + spec.securityTags = plug.SecurityTags() + defer func() { spec.securityTags = nil }() return iface.UDevPermanentPlug(spec, plug) } return nil @@ -89,6 +109,8 @@ UDevPermanentSlot(spec *Specification, slot *interfaces.Slot) error } if iface, ok := iface.(definer); ok { + spec.securityTags = slot.SecurityTags() + defer func() { spec.securityTags = nil }() return iface.UDevPermanentSlot(spec, slot) } return nil diff -Nru snapd-2.28.5/interfaces/udev/spec_test.go snapd-2.29.3/interfaces/udev/spec_test.go --- snapd-2.28.5/interfaces/udev/spec_test.go 2017-08-18 13:48:10.000000000 +0000 +++ snapd-2.29.3/interfaces/udev/spec_test.go 2017-11-09 12:08:52.000000000 +0000 @@ -25,7 +25,7 @@ "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/ifacetest" "github.com/snapcore/snapd/interfaces/udev" - "github.com/snapcore/snapd/snap" + "github.com/snapcore/snapd/snap/snaptest" ) type specSuite struct { @@ -55,22 +55,28 @@ return nil }, }, - plug: &interfaces.Plug{ - PlugInfo: &snap.PlugInfo{ - Snap: &snap.Info{SuggestedName: "snap1"}, - Name: "name", - Interface: "test", - }, - }, - slot: &interfaces.Slot{ - SlotInfo: &snap.SlotInfo{ - Snap: &snap.Info{SuggestedName: "snap2"}, - Name: "name", - Interface: "test", - }, - }, }) +func (s *specSuite) SetUpSuite(c *C) { + info1 := snaptest.MockInfo(c, `name: snap1 +plugs: + name: + interface: test +apps: + foo: + command: bin/foo +hooks: + configure: +`, nil) + info2 := snaptest.MockInfo(c, `name: snap2 +slots: + name: + interface: test +`, nil) + s.plug = &interfaces.Plug{PlugInfo: info1.Plugs["name"]} + s.slot = &interfaces.Slot{SlotInfo: info2.Slots["name"]} +} + func (s *specSuite) SetUpTest(c *C) { s.spec = &udev.Specification{} } @@ -80,6 +86,24 @@ c.Assert(s.spec.Snippets(), DeepEquals, []string{"foo"}) } +func (s *specSuite) TestTagDevice(c *C) { + // TagDevice acts in the scope of the plug/slot (as appropriate) and + // affects all of the apps and hooks related to the given plug or slot + // (with the exception that slots cannot have hooks). + iface := &ifacetest.TestInterface{ + InterfaceName: "test", + UDevConnectedPlugCallback: func(spec *udev.Specification, plug *interfaces.Plug, plugAttrs map[string]interface{}, slot *interfaces.Slot, slotAttrs map[string]interface{}) error { + spec.TagDevice(`kernel="voodoo"`) + return nil + }, + } + c.Assert(s.spec.AddConnectedPlug(iface, s.plug, nil, s.slot, nil), IsNil) + c.Assert(s.spec.Snippets(), DeepEquals, []string{ + `kernel="voodoo", TAG+="snap_snap1_foo"`, + `kernel="voodoo", TAG+="snap_snap1_hook_configure"`, + }) +} + // The spec.Specification can be used through the interfaces.Specification interface func (s *specSuite) TestSpecificationIface(c *C) { var r interfaces.Specification = s.spec diff -Nru snapd-2.28.5/logger/logger.go snapd-2.29.3/logger/logger.go --- snapd-2.28.5/logger/logger.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/logger/logger.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,6 +20,7 @@ package logger import ( + "bytes" "fmt" "io" "log" @@ -86,6 +87,21 @@ logger.Debug(msg) } +// MockLogger replaces the exiting logger with a buffer and returns +// the log buffer and a restore function. +func MockLogger() (buf *bytes.Buffer, restore func()) { + buf = &bytes.Buffer{} + oldLogger := logger + l, err := New(buf, DefaultFlags) + if err != nil { + panic(err) + } + SetLogger(l) + return buf, func() { + SetLogger(oldLogger) + } +} + // SetLogger sets the global logger to the given one func SetLogger(l Logger) { lock.Lock() diff -Nru snapd-2.28.5/logger/logger_test.go snapd-2.29.3/logger/logger_test.go --- snapd-2.28.5/logger/logger_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/logger/logger_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -36,16 +36,16 @@ var _ = Suite(&LogSuite{}) type LogSuite struct { - oldLogger logger.Logger + logbuf *bytes.Buffer + restoreLogger func() } func (s *LogSuite) SetUpTest(c *C) { - s.oldLogger = logger.GetLogger() - logger.SetLogger(logger.NullLogger) + s.logbuf, s.restoreLogger = logger.MockLogger() } func (s *LogSuite) TearDownTest(c *C) { - logger.SetLogger(s.oldLogger) + s.restoreLogger() } func (s *LogSuite) TestDefault(c *C) { @@ -67,48 +67,24 @@ } func (s *LogSuite) TestDebugf(c *C) { - var logbuf bytes.Buffer - l, err := logger.New(&logbuf, logger.DefaultFlags) - c.Assert(err, IsNil) - - logger.SetLogger(l) - logger.Debugf("xyzzy") - c.Check(logbuf.String(), Equals, "") + c.Check(s.logbuf.String(), Equals, "") } func (s *LogSuite) TestDebugfEnv(c *C) { - var logbuf bytes.Buffer - l, err := logger.New(&logbuf, logger.DefaultFlags) - c.Assert(err, IsNil) - - logger.SetLogger(l) - os.Setenv("SNAPD_DEBUG", "1") defer os.Unsetenv("SNAPD_DEBUG") logger.Debugf("xyzzy") - c.Check(logbuf.String(), testutil.Contains, `DEBUG: xyzzy`) + c.Check(s.logbuf.String(), testutil.Contains, `DEBUG: xyzzy`) } func (s *LogSuite) TestNoticef(c *C) { - var logbuf bytes.Buffer - l, err := logger.New(&logbuf, logger.DefaultFlags) - c.Assert(err, IsNil) - - logger.SetLogger(l) - logger.Noticef("xyzzy") - c.Check(logbuf.String(), Matches, `(?m).*logger_test\.go:\d+: xyzzy`) + c.Check(s.logbuf.String(), Matches, `(?m).*logger_test\.go:\d+: xyzzy`) } func (s *LogSuite) TestPanicf(c *C) { - var logbuf bytes.Buffer - l, err := logger.New(&logbuf, logger.DefaultFlags) - c.Assert(err, IsNil) - - logger.SetLogger(l) - c.Check(func() { logger.Panicf("xyzzy") }, Panics, "xyzzy") - c.Check(logbuf.String(), Matches, `(?m).*logger_test\.go:\d+: PANIC xyzzy`) + c.Check(s.logbuf.String(), Matches, `(?m).*logger_test\.go:\d+: PANIC xyzzy`) } diff -Nru snapd-2.28.5/osutil/cp_test.go snapd-2.29.3/osutil/cp_test.go --- snapd-2.28.5/osutil/cp_test.go 2016-09-09 06:45:37.000000000 +0000 +++ snapd-2.29.3/osutil/cp_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -63,7 +63,7 @@ } } - return + return err } func (s *cpSuite) SetUpTest(c *C) { diff -Nru snapd-2.28.5/osutil/env.go snapd-2.29.3/osutil/env.go --- snapd-2.28.5/osutil/env.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/osutil/env.go 2017-10-23 06:17:27.000000000 +0000 @@ -65,6 +65,19 @@ return 0 } +// EnvMap takes a list of "key=value" strings and transforms them into +// a map. +func EnvMap(env []string) map[string]string { + out := make(map[string]string, len(env)) + for _, kv := range env { + l := strings.SplitN(kv, "=", 2) + if len(l) == 2 { + out[l[0]] = l[1] + } + } + return out +} + // SubstituteEnv takes a list of environment strings like: // - K1=BAR // - K2=$K1 diff -Nru snapd-2.28.5/osutil/env_test.go snapd-2.29.3/osutil/env_test.go --- snapd-2.28.5/osutil/env_test.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/osutil/env_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -132,3 +132,31 @@ c.Check(strings.Join(env, ","), check.DeepEquals, t.expected, check.Commentf("invalid result for %q, got %q expected %q", t.env, env, t.expected)) } } + +func (s *envSuite) TestEnvMap(c *check.C) { + for _, t := range []struct { + env []string + expected map[string]string + }{ + { + []string{"K=V"}, + map[string]string{"K": "V"}, + }, + { + []string{"K=V=V=V"}, + map[string]string{"K": "V=V=V"}, + }, + { + []string{"K1=V1", "K2=V2"}, + map[string]string{"K1": "V1", "K2": "V2"}, + }, + { + // invalid input is handled gracefully + []string{"KEY_ONLY"}, + map[string]string{}, + }, + } { + m := osutil.EnvMap(t.env) + c.Check(m, check.DeepEquals, t.expected, check.Commentf("invalid result for %q, got %q expected %q", t.env, m, t.expected)) + } +} diff -Nru snapd-2.28.5/osutil/exec.go snapd-2.29.3/osutil/exec.go --- snapd-2.28.5/osutil/exec.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/osutil/exec.go 2017-10-23 06:17:27.000000000 +0000 @@ -147,11 +147,11 @@ cmdWaitTimeout = 5 * time.Second ) -// killProcessGroup kills the process group associated with the given command. +// KillProcessGroup kills the process group associated with the given command. // // If the command hasn't had Setpgid set in its SysProcAttr, you'll probably end // up killing yourself. -func killProcessGroup(cmd *exec.Cmd) error { +func KillProcessGroup(cmd *exec.Cmd) error { pgid, err := syscallGetpgid(cmd.Process.Pid) if err != nil { return err @@ -220,7 +220,7 @@ // select above exited which means that aborted or killTimeout // was reached. Kill the command and wait for command.Wait() // to clean it up (but limit the wait with the cmdWaitTimer) - if err := killProcessGroup(command); err != nil { + if err := KillProcessGroup(command); err != nil { return nil, fmt.Errorf("cannot abort: %s", err) } select { diff -Nru snapd-2.28.5/osutil/export_test.go snapd-2.29.3/osutil/export_test.go --- snapd-2.28.5/osutil/export_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/osutil/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -21,6 +21,7 @@ import ( "io" + "os" "os/exec" "os/user" "syscall" @@ -79,9 +80,27 @@ } } -var KillProcessGroup = killProcessGroup - func WaitingReaderGuts(r io.Reader) (io.Reader, *exec.Cmd) { wr := r.(*waitingReader) return wr.reader, wr.cmd } + +func MockChown(f func(*os.File, int, int) error) func() { + oldChown := chown + chown = f + return func() { + chown = oldChown + } +} + +func SetAtomicFileRenamed(aw *AtomicFile, renamed bool) { + aw.renamed = renamed +} + +func SetUnsafeIO(b bool) func() { + oldSnapdUnsafeIO := snapdUnsafeIO + snapdUnsafeIO = b + return func() { + snapdUnsafeIO = oldSnapdUnsafeIO + } +} diff -Nru snapd-2.28.5/osutil/group.go snapd-2.29.3/osutil/group.go --- snapd-2.28.5/osutil/group.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/osutil/group.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,158 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package osutil + +// #include +// #include +// #include +// #include +import "C" + +import ( + "fmt" + "os/user" + "strconv" + "syscall" + "unsafe" +) + +// hrm, user.LookupGroup() doesn't exist yet: +// https://github.com/golang/go/issues/2617 +// +// Use implementation from upcoming releases: +// https://golang.org/src/os/user/lookup_unix.go +func lookupGroup(groupname string) (string, error) { + var grp C.struct_group + var result *C.struct_group + + buf := alloc(groupBuffer) + defer buf.free() + cname := C.CString(groupname) + defer C.free(unsafe.Pointer(cname)) + + err := retryWithBuffer(buf, func() syscall.Errno { + return syscall.Errno(C.getgrnam_r(cname, + &grp, + (*C.char)(buf.ptr), + C.size_t(buf.size), + &result)) + }) + if err != nil { + return "", fmt.Errorf("group: lookup groupname %s: %v", groupname, err) + } + if result == nil { + return "", fmt.Errorf("group: unknown group %s", groupname) + } + return strconv.Itoa(int(grp.gr_gid)), nil +} + +type bufferKind C.int + +const ( + groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX) +) + +func (k bufferKind) initialSize() C.size_t { + sz := C.sysconf(C.int(k)) + if sz == -1 { + // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX. + // Additionally, not all Linux systems have it, either. For + // example, the musl libc returns -1. + return 1024 + } + if !isSizeReasonable(int64(sz)) { + // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run. + return maxBufferSize + } + return C.size_t(sz) +} + +type memBuffer struct { + ptr unsafe.Pointer + size C.size_t +} + +func alloc(kind bufferKind) *memBuffer { + sz := kind.initialSize() + return &memBuffer{ + ptr: C.malloc(sz), + size: sz, + } +} + +func (mb *memBuffer) resize(newSize C.size_t) { + mb.ptr = C.realloc(mb.ptr, newSize) + mb.size = newSize +} + +func (mb *memBuffer) free() { + C.free(mb.ptr) +} + +// retryWithBuffer repeatedly calls f(), increasing the size of the +// buffer each time, until f succeeds, fails with a non-ERANGE error, +// or the buffer exceeds a reasonable limit. +func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error { + for { + errno := f() + if errno == 0 { + return nil + } else if errno != syscall.ERANGE { + return errno + } + newSize := buf.size * 2 + if !isSizeReasonable(int64(newSize)) { + return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize) + } + buf.resize(newSize) + } +} + +const maxBufferSize = 1 << 20 + +func isSizeReasonable(sz int64) bool { + return sz > 0 && sz <= maxBufferSize +} + +// end code from https://golang.org/src/os/user/lookup_unix.go + +// FindUid returns the identifier of the given UNIX user name. +func FindUid(username string) (uint64, error) { + user, err := user.Lookup(username) + if err != nil { + return 0, err + } + + return strconv.ParseUint(user.Uid, 10, 64) +} + +// FindGid returns the identifier of the given UNIX group name. +func FindGid(group string) (uint64, error) { + // In golang 1.8 we can use the built-in function like this: + //group, err := user.LookupGroup(group) + group, err := lookupGroup(group) + if err != nil { + return 0, err + } + + // In golang 1.8 we can parse the group.Gid string instead. + //return strconv.ParseUint(group.Gid, 10, 64) + return strconv.ParseUint(group, 10, 64) +} diff -Nru snapd-2.28.5/osutil/io.go snapd-2.29.3/osutil/io.go --- snapd-2.28.5/osutil/io.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/osutil/io.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,7 +20,9 @@ package osutil import ( + "bytes" "errors" + "io" "os" "path/filepath" "strings" @@ -39,18 +41,45 @@ // Allow disabling sync for testing. This brings massive improvements on // certain filesystems (like btrfs) and very much noticeable improvements in // all unit tests in genreal. -var snapdUnsafeIO bool = len(os.Args) > 0 && strings.HasSuffix(os.Args[0], ".test") && GetenvBool("SNAPD_UNSAFE_IO") +var snapdUnsafeIO bool = len(os.Args) > 0 && strings.HasSuffix(os.Args[0], ".test") && GetenvBool("SNAPD_UNSAFE_IO", true) -// AtomicWriteFile updates the filename atomically and works otherwise -// like io/ioutil.WriteFile() -// -// Note that it won't follow symlinks and will replace existing symlinks -// with the real file -func AtomicWriteFile(filename string, data []byte, perm os.FileMode, flags AtomicWriteFlags) (err error) { - return AtomicWriteFileChown(filename, data, perm, flags, -1, -1) +// An AtomicFile is similar to an os.File but it has an additional +// Commit() method that does whatever needs to be done so the +// modification is "atomic": an AtomicFile will do its best to leave +// either the previous content or the new content in permanent +// storage. It also has a Cancel() method to abort and clean up. +type AtomicFile struct { + *os.File + + target string + tmpname string + uid int + gid int + closed bool + renamed bool } -func AtomicWriteFileChown(filename string, data []byte, perm os.FileMode, flags AtomicWriteFlags, uid, gid int) (err error) { +// NewAtomicFile builds an AtomicFile backed by an *os.File that will have +// the given filename, permissions and uid/gid when Committed. +// +// It _might_ be implemented using O_TMPFILE (see open(2)). +// +// Note that it won't follow symlinks and will replace existing symlinks with +// the real file, unless the AtomicWriteFollow flag is specified. +// +// It is the caller's responsibility to clean up on error, by calling Cancel(). +// +// It is also the caller's responsibility to coordinate access to this, if it +// is used from different goroutines. +// +// Also note that there are a number of scenarios where Commit fails and then +// Cancel also fails. In all these scenarios your filesystem was probably in a +// rather poor state. Good luck. +func NewAtomicFile(filename string, perm os.FileMode, flags AtomicWriteFlags, uid, gid int) (aw *AtomicFile, err error) { + if (uid < 0) != (gid < 0) { + return nil, errors.New("internal error: AtomicFile needs none or both of uid and gid set") + } + if flags&AtomicWriteFollow != 0 { if fn, err := os.Readlink(filename); err == nil || (fn != "" && os.IsNotExist(err)) { if filepath.IsAbs(fn) { @@ -62,53 +91,132 @@ } tmp := filename + "." + strutil.MakeRandomString(12) - // XXX: if go switches to use aio_fsync, we need to open the dir for writing - dir, err := os.Open(filepath.Dir(filename)) - if err != nil { - return err - } - defer dir.Close() - fd, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC|os.O_EXCL, perm) if err != nil { - return err + return nil, err } - defer func() { - e := fd.Close() - if err == nil { - err = e - } - if err != nil { - os.Remove(tmp) - } - }() - // according to the docs, Write returns a non-nil error when n != - // len(b), so don't worry about short writes. - if _, err := fd.Write(data); err != nil { - return err + return &AtomicFile{ + File: fd, + target: filename, + tmpname: tmp, + uid: uid, + gid: gid, + }, nil +} + +// ErrCannotCancel means the Commit operation failed at the last step, and +// your luck has run out. +var ErrCannotCancel = errors.New("cannot cancel: file has already been renamed") + +func (aw *AtomicFile) Close() error { + aw.closed = true + return aw.File.Close() +} + +// Cancel closes the AtomicWriter, and cleans up any artifacts. Cancel +// can fail if Commit() was (even partially) successful, but calling +// Cancel after a successful Commit does nothing beyond returning +// error--so it's always safe to defer a Cancel(). +func (aw *AtomicFile) Cancel() error { + if aw.renamed { + return ErrCannotCancel + } + + var e1, e2 error + if aw.tmpname != "" { + e1 = os.Remove(aw.tmpname) + } + if !aw.closed { + e2 = aw.Close() + } + if e1 != nil { + return e1 } + return e2 +} + +var chown = (*os.File).Chown - if uid > -1 && gid > -1 { - if err := fd.Chown(uid, gid); err != nil { +// Commit the modification; make it permanent. +// +// If Commit succeeds, the writer is closed and further attempts to +// write will fail. If Commit fails, the writer _might_ be closed; +// Cancel() needs to be called to clean up. +func (aw *AtomicFile) Commit() error { + if aw.uid > -1 && aw.gid > -1 { + if err := chown(aw.File, aw.uid, aw.gid); err != nil { return err } - } else if uid > -1 || gid > -1 { - return errors.New("internal error: AtomicWriteFileChown needs none or both of uid and gid set") } + var dir *os.File if !snapdUnsafeIO { - if err := fd.Sync(); err != nil { + // XXX: if go switches to use aio_fsync, we need to open the dir for writing + d, err := os.Open(filepath.Dir(aw.target)) + if err != nil { + return err + } + dir = d + defer dir.Close() + + if err := aw.Sync(); err != nil { return err } } - if err := os.Rename(tmp, filename); err != nil { + if err := aw.Close(); err != nil { + return err + } + + if err := os.Rename(aw.tmpname, aw.target); err != nil { return err } + aw.renamed = true // it is now too late to Cancel() if !snapdUnsafeIO { return dir.Sync() } + return nil } + +// The AtomicWrite* family of functions work like ioutil.WriteFile(), but the +// file created is an AtomicWriter, which is Committed before returning. +// +// AtomicWriteChown and AtomicWriteFileChown take an uid and a gid that can be +// used to specify the ownership of the created file. They must be both +// non-negative (in which case chown is called), or both negative (in which +// case it isn't). +// +// AtomicWriteFile and AtomicWriteFileChown take the content to be written as a +// []byte, and so work exactly like io.WriteFile(); AtomicWrite and +// AtomicWriteChown take an io.Reader which is copied into the file instead, +// and so are more amenable to streaming. +func AtomicWrite(filename string, reader io.Reader, perm os.FileMode, flags AtomicWriteFlags) (err error) { + return AtomicWriteChown(filename, reader, perm, flags, -1, -1) +} + +func AtomicWriteFile(filename string, data []byte, perm os.FileMode, flags AtomicWriteFlags) (err error) { + return AtomicWriteChown(filename, bytes.NewReader(data), perm, flags, -1, -1) +} + +func AtomicWriteFileChown(filename string, data []byte, perm os.FileMode, flags AtomicWriteFlags, uid, gid int) (err error) { + return AtomicWriteChown(filename, bytes.NewReader(data), perm, flags, uid, gid) +} + +func AtomicWriteChown(filename string, reader io.Reader, perm os.FileMode, flags AtomicWriteFlags, uid, gid int) (err error) { + aw, err := NewAtomicFile(filename, perm, flags, uid, gid) + if err != nil { + return err + } + + // Cancel once Committed is a NOP :-) + defer aw.Cancel() + + if _, err := io.Copy(aw, reader); err != nil { + return err + } + + return aw.Commit() +} diff -Nru snapd-2.28.5/osutil/io_test.go snapd-2.29.3/osutil/io_test.go --- snapd-2.28.5/osutil/io_test.go 2017-09-11 07:15:11.000000000 +0000 +++ snapd-2.29.3/osutil/io_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -17,14 +17,16 @@ * */ -package osutil +package osutil_test import ( + "errors" "io/ioutil" "math/rand" "os" "path/filepath" + "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/strutil" . "gopkg.in/check.v1" @@ -38,7 +40,7 @@ tmpdir := c.MkDir() p := filepath.Join(tmpdir, "foo") - err := AtomicWriteFile(p, []byte("canary"), 0644, 0) + err := osutil.AtomicWriteFile(p, []byte("canary"), 0644, 0) c.Assert(err, IsNil) content, err := ioutil.ReadFile(p) @@ -55,7 +57,7 @@ tmpdir := c.MkDir() p := filepath.Join(tmpdir, "foo") - err := AtomicWriteFile(p, []byte(""), 0600, 0) + err := osutil.AtomicWriteFile(p, []byte(""), 0600, 0) c.Assert(err, IsNil) st, err := os.Stat(p) @@ -67,7 +69,7 @@ tmpdir := c.MkDir() p := filepath.Join(tmpdir, "foo") c.Assert(ioutil.WriteFile(p, []byte("hello"), 0644), IsNil) - c.Assert(AtomicWriteFile(p, []byte("hi"), 0600, 0), IsNil) + c.Assert(osutil.AtomicWriteFile(p, []byte("hi"), 0600, 0), IsNil) content, err := ioutil.ReadFile(p) c.Assert(err, IsNil) @@ -84,7 +86,7 @@ c.Assert(os.Chmod(rodir, 0500), IsNil) defer os.Chmod(rodir, 0700) - err := AtomicWriteFile(p, []byte("hi"), 0600, 0) + err := osutil.AtomicWriteFile(p, []byte("hi"), 0600, 0) c.Assert(err, NotNil) } @@ -98,7 +100,7 @@ c.Assert(os.Chmod(rodir, 0500), IsNil) defer os.Chmod(rodir, 0700) - err := AtomicWriteFile(p, []byte("hi"), 0600, AtomicWriteFollow) + err := osutil.AtomicWriteFile(p, []byte("hi"), 0600, osutil.AtomicWriteFollow) c.Assert(err, IsNil) content, err := ioutil.ReadFile(p) @@ -117,7 +119,7 @@ defer os.Chmod(rodir, 0700) c.Assert(ioutil.WriteFile(s, []byte("hello"), 0644), IsNil) - c.Assert(AtomicWriteFile(p, []byte("hi"), 0600, AtomicWriteFollow), IsNil) + c.Assert(osutil.AtomicWriteFile(p, []byte("hi"), 0600, osutil.AtomicWriteFollow), IsNil) content, err := ioutil.ReadFile(p) c.Assert(err, IsNil) @@ -133,7 +135,7 @@ c.Assert(os.Chmod(rodir, 0500), IsNil) defer os.Chmod(rodir, 0700) - err := AtomicWriteFile(p, []byte("hi"), 0600, AtomicWriteFollow) + err := osutil.AtomicWriteFile(p, []byte("hi"), 0600, osutil.AtomicWriteFollow) c.Assert(err, IsNil) content, err := ioutil.ReadFile(p) @@ -152,7 +154,7 @@ defer os.Chmod(rodir, 0700) c.Assert(ioutil.WriteFile(s, []byte("hello"), 0644), IsNil) - c.Assert(AtomicWriteFile(p, []byte("hi"), 0600, AtomicWriteFollow), IsNil) + c.Assert(osutil.AtomicWriteFile(p, []byte("hi"), 0600, osutil.AtomicWriteFollow), IsNil) content, err := ioutil.ReadFile(p) c.Assert(err, IsNil) @@ -171,6 +173,100 @@ err := ioutil.WriteFile(p+"."+expectedRandomness, []byte(""), 0644) c.Assert(err, IsNil) - err = AtomicWriteFile(p, []byte(""), 0600, 0) + err = osutil.AtomicWriteFile(p, []byte(""), 0600, 0) c.Assert(err, ErrorMatches, "open .*: file exists") } + +func (ts *AtomicWriteTestSuite) TestAtomicFileUidGidError(c *C) { + d := c.MkDir() + p := filepath.Join(d, "foo") + + _, err := osutil.NewAtomicFile(p, 0644, 0, -1, 1) + c.Check(err, ErrorMatches, ".*needs none or both of uid and gid set") +} + +func (ts *AtomicWriteTestSuite) TestAtomicFileChownError(c *C) { + eUid := 42 + eGid := 74 + eErr := errors.New("this didn't work") + defer osutil.MockChown(func(fd *os.File, uid int, gid int) error { + c.Check(uid, Equals, eUid) + c.Check(gid, Equals, eGid) + return eErr + })() + + d := c.MkDir() + p := filepath.Join(d, "foo") + + aw, err := osutil.NewAtomicFile(p, 0644, 0, eUid, eGid) + c.Assert(err, IsNil) + defer aw.Cancel() + + _, err = aw.Write([]byte("hello")) + c.Assert(err, IsNil) + + c.Check(aw.Commit(), Equals, eErr) +} + +func (ts *AtomicWriteTestSuite) TestAtomicFileCancelError(c *C) { + d := c.MkDir() + p := filepath.Join(d, "foo") + aw, err := osutil.NewAtomicFile(p, 0644, 0, -1, -1) + c.Assert(err, IsNil) + + c.Assert(aw.File.Close(), IsNil) + // Depending on golang version the error is one of the two. + c.Check(aw.Cancel(), ErrorMatches, "invalid argument|file already closed") +} + +func (ts *AtomicWriteTestSuite) TestAtomicFileCancelBadError(c *C) { + d := c.MkDir() + p := filepath.Join(d, "foo") + aw, err := osutil.NewAtomicFile(p, 0644, 0, -1, -1) + c.Assert(err, IsNil) + defer aw.Close() + + osutil.SetAtomicFileRenamed(aw, true) + + c.Check(aw.Cancel(), Equals, osutil.ErrCannotCancel) +} + +func (ts *AtomicWriteTestSuite) TestAtomicFileCancelNoClose(c *C) { + d := c.MkDir() + p := filepath.Join(d, "foo") + aw, err := osutil.NewAtomicFile(p, 0644, 0, -1, -1) + c.Assert(err, IsNil) + c.Assert(aw.Close(), IsNil) + + c.Check(aw.Cancel(), IsNil) +} + +func (ts *AtomicWriteTestSuite) TestAtomicFileCancel(c *C) { + d := c.MkDir() + p := filepath.Join(d, "foo") + + aw, err := osutil.NewAtomicFile(p, 0644, 0, -1, -1) + c.Assert(err, IsNil) + fn := aw.File.Name() + c.Check(osutil.FileExists(fn), Equals, true) + c.Check(aw.Cancel(), IsNil) + c.Check(osutil.FileExists(fn), Equals, false) +} + +// SafeIoAtomicWriteTestSuite runs all AtomicWrite with safe +// io enabled +type SafeIoAtomicWriteTestSuite struct { + AtomicWriteTestSuite + + restoreUnsafeIO func() +} + +var _ = Suite(&SafeIoAtomicWriteTestSuite{}) + +func (s *SafeIoAtomicWriteTestSuite) SetUpSuite(c *C) { + s.restoreUnsafeIO = osutil.SetUnsafeIO(false) +} + +func (s *SafeIoAtomicWriteTestSuite) TearDownSuite(c *C) { + s.restoreUnsafeIO() +} diff -Nru snapd-2.28.5/osutil/osutil_test.go snapd-2.29.3/osutil/osutil_test.go --- snapd-2.28.5/osutil/osutil_test.go 2016-08-11 17:27:59.000000000 +0000 +++ snapd-2.29.3/osutil/osutil_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -1,3 +1,22 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + package osutil_test import ( diff -Nru snapd-2.28.5/overlord/assertstate/assertmgr.go snapd-2.29.3/overlord/assertstate/assertmgr.go --- snapd-2.28.5/overlord/assertstate/assertmgr.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/overlord/assertstate/assertmgr.go 2017-10-23 06:17:27.000000000 +0000 @@ -29,7 +29,6 @@ "github.com/snapcore/snapd/asserts/sysdb" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" - "github.com/snapcore/snapd/store" ) // AssertManager is responsible for the enforcement of assertions in @@ -42,6 +41,8 @@ // Manager returns a new assertion manager. func Manager(s *state.State) (*AssertManager, error) { + delayedCrossMgrInit() + runner := state.NewTaskRunner(s) runner.AddHandler("validate-snap", doValidateSnap, nil) @@ -112,8 +113,8 @@ err = doFetch(t.State(), snapsup.UserID, func(f asserts.Fetcher) error { return snapasserts.FetchSnapAssertions(f, sha3_384) }) - if notFound, ok := err.(*store.AssertionNotFoundError); ok { - if notFound.Ref.Type == asserts.SnapRevisionType { + if notFound, ok := err.(*asserts.NotFoundError); ok { + if notFound.Type == asserts.SnapRevisionType { return fmt.Errorf("cannot verify snap %q, no matching signatures found", snapsup.Name()) } else { return fmt.Errorf("cannot find supported signatures to verify snap %q and its hash (%v)", snapsup.Name(), notFound) diff -Nru snapd-2.28.5/overlord/assertstate/assertstate.go snapd-2.29.3/overlord/assertstate/assertstate.go --- snapd-2.28.5/overlord/assertstate/assertstate.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/overlord/assertstate/assertstate.go 2017-11-02 15:44:20.000000000 +0000 @@ -33,7 +33,6 @@ "github.com/snapcore/snapd/overlord/state" "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" - "github.com/snapcore/snapd/store" ) // Add the given assertion to the system assertion database. @@ -105,12 +104,12 @@ db := cachedDB(st) retrieve := func(ref *asserts.Ref) (asserts.Assertion, error) { a, err := b.bs.Get(ref.Type, ref.PrimaryKey, ref.Type.MaxSupportedFormat()) - if err == asserts.ErrNotFound { + if asserts.IsNotFound(err) { // fallback to pre-existing assertions a, err = ref.Resolve(db.Find) } if err != nil { - return nil, fmt.Errorf("cannot find %s: %s", ref, err) + return nil, findError("cannot find %s", ref, err) } return a, nil } @@ -129,6 +128,14 @@ return f.commit() } +func findError(format string, ref *asserts.Ref, err error) error { + if asserts.IsNotFound(err) { + return fmt.Errorf(format, ref) + } else { + return fmt.Errorf(format+": %v", ref, err) + } +} + // RefreshSnapDeclarations refetches all the current snap declarations and their prerequisites. func RefreshSnapDeclarations(s *state.State, userID int) error { snapStates, err := snapstate.All(s) @@ -168,8 +175,8 @@ return fmt.Sprintf("refresh control errors:%s", strings.Join(l, "\n - ")) } -// ValidateRefreshes validates the refresh candidate revisions represented by the snapInfos, looking for the needed refresh control validation assertions, it returns a validated subset in validated and a summary error if not all candidates validated. -func ValidateRefreshes(s *state.State, snapInfos []*snap.Info, userID int) (validated []*snap.Info, err error) { +// ValidateRefreshes validates the refresh candidate revisions represented by the snapInfos, looking for the needed refresh control validation assertions, it returns a validated subset in validated and a summary error if not all candidates validated. ignoreValidation is a set of snap-ids that should not be gated. +func ValidateRefreshes(s *state.State, snapInfos []*snap.Info, ignoreValidation map[string]bool, userID int) (validated []*snap.Info, err error) { // maps gated snap-ids to gating snap-ids controlled := make(map[string][]string) // maps gating snap-ids to their snap names @@ -194,7 +201,7 @@ "snap-id": gatingID, }) if err != nil { - return nil, fmt.Errorf("internal error: cannot find snap declaration for installed snap %q (id %q): err", snapName, gatingID) + return nil, fmt.Errorf("internal error: cannot find snap declaration for installed snap %q: %v", snapName, err) } decl := a.(*asserts.SnapDeclaration) control := decl.RefreshControl() @@ -203,7 +210,9 @@ } gatingNames[gatingID] = decl.SnapName() for _, gatedID := range control { - controlled[gatedID] = append(controlled[gatedID], gatingID) + if !ignoreValidation[gatedID] { + controlled[gatedID] = append(controlled[gatedID], gatingID) + } } } @@ -225,7 +234,7 @@ PrimaryKey: []string{release.Series, gatingID, gatedID, candInfo.Revision.String()}, } err := f.Fetch(valref) - if notFound, ok := err.(*store.AssertionNotFoundError); ok && notFound.Ref.Type == asserts.ValidationType { + if notFound, ok := err.(*asserts.NotFoundError); ok && notFound.Type == asserts.ValidationType { return fmt.Errorf("no validation by %q", gatingNames[gatingID]) } if err != nil { @@ -245,7 +254,7 @@ for _, valref := range validationRefs { a, err := valref.Resolve(db.Find) if err != nil { - return nil, fmt.Errorf("internal error: cannot find just fetched %v: %v", valref, err) + return nil, findError("internal error: cannot find just fetched %v", valref, err) } if val := a.(*asserts.Validation); val.Revoked() { revoked = val @@ -267,20 +276,13 @@ return validated, nil } -func init() { - // hook validation of refreshes into snapstate logic - snapstate.ValidateRefreshes = ValidateRefreshes - // hook auto refresh of assertions into snapstate - snapstate.AutoRefreshAssertions = AutoRefreshAssertions -} - // BaseDeclaration returns the base-declaration assertion with policies governing all snaps. func BaseDeclaration(s *state.State) (*asserts.BaseDeclaration, error) { // TODO: switch keeping this in the DB and have it revisioned/updated // via the store baseDecl := asserts.BuiltinBaseDeclaration() if baseDecl == nil { - return nil, asserts.ErrNotFound + return nil, &asserts.NotFoundError{Type: asserts.BaseDeclarationType} } return baseDecl, nil } @@ -351,7 +353,11 @@ return res, nil } -func init() { +func delayedCrossMgrInit() { + // hook validation of refreshes into snapstate logic + snapstate.ValidateRefreshes = ValidateRefreshes + // hook auto refresh of assertions into snapstate + snapstate.AutoRefreshAssertions = AutoRefreshAssertions // hook retrieving auto-aliases into snapstate logic snapstate.AutoAliases = AutoAliases } diff -Nru snapd-2.28.5/overlord/assertstate/assertstate_test.go snapd-2.29.3/overlord/assertstate/assertstate_test.go --- snapd-2.28.5/overlord/assertstate/assertstate_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/assertstate/assertstate_test.go 2017-11-02 15:44:20.000000000 +0000 @@ -36,19 +36,21 @@ "github.com/snapcore/snapd/asserts/assertstest" "github.com/snapcore/snapd/asserts/sysdb" "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/overlord" "github.com/snapcore/snapd/overlord/assertstate" "github.com/snapcore/snapd/overlord/auth" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/snap/snaptest" - "github.com/snapcore/snapd/store" "github.com/snapcore/snapd/store/storetest" ) func TestAssertManager(t *testing.T) { TestingT(t) } type assertMgrSuite struct { + o *overlord.Overlord state *state.State mgr *assertstate.AssertManager @@ -77,11 +79,7 @@ func (sto *fakeStore) Assertion(assertType *asserts.AssertionType, key []string, _ *auth.UserState) (asserts.Assertion, error) { sto.pokeStateLock() ref := &asserts.Ref{Type: assertType, PrimaryKey: key} - a, err := ref.Resolve(sto.db.Find) - if err != nil { - return nil, &store.AssertionNotFoundError{Ref: ref} - } - return a, nil + return ref.Resolve(sto.db.Find) } func (s *assertMgrSuite) SetUpTest(c *C) { @@ -102,13 +100,15 @@ s.dev1Signing = assertstest.NewSigningDB(s.dev1Acct.AccountID(), dev1PrivKey) - s.state = state.New(nil) + s.o = overlord.Mock() + s.state = s.o.State() mgr, err := assertstate.Manager(s.state) c.Assert(err, IsNil) s.mgr = mgr + s.o.AddManager(s.mgr) s.state.Lock() - snapstate.ReplaceStore(s.state, &fakeStore{ + storestate.ReplaceStore(s.state, &fakeStore{ state: s.state, db: s.storeSigning, }) @@ -262,6 +262,7 @@ "authority-id": "can0nical", "brand-id": "can0nical", "repair-id": "2", + "summary": "repair two", "timestamp": time.Now().UTC().Format(time.RFC3339), } repair, err := aSignDB.Sign(asserts.RepairType, headers, []byte("#script"), "") @@ -406,12 +407,9 @@ c.Check(snapRev.(*asserts.SnapRevision).SnapRevision(), Equals, 11) } -func (s *assertMgrSuite) settle() { - // XXX: would like to use Overlord.Settle but not enough control there - for i := 0; i < 50; i++ { - s.mgr.Ensure() - s.mgr.Wait() - } +func (s *assertMgrSuite) settle(c *C) { + err := s.o.Settle(5 * time.Second) + c.Assert(err, IsNil) } func (s *assertMgrSuite) TestValidateSnap(c *C) { @@ -441,7 +439,7 @@ s.state.Unlock() defer s.mgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Assert(chg.Err(), IsNil) @@ -479,7 +477,7 @@ s.state.Unlock() defer s.mgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Assert(chg.Err(), ErrorMatches, `(?s).*cannot verify snap "foo", no matching signatures found.*`) @@ -512,7 +510,7 @@ s.state.Unlock() defer s.mgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Assert(chg.Err(), ErrorMatches, `(?s).*cannot install snap "f" that is undergoing a rename to "foo".*`) @@ -566,7 +564,7 @@ s.state.Unlock() defer s.mgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Assert(chg.Err(), ErrorMatches, `(?s).*proposed "snap-declaration" assertion has format 999 but 0 is latest supported.*`) @@ -711,7 +709,7 @@ s.state.Lock() defer s.state.Unlock() - validated, err := assertstate.ValidateRefreshes(s.state, nil, 0) + validated, err := assertstate.ValidateRefreshes(s.state, nil, nil, 0) c.Assert(err, IsNil) c.Check(validated, HasLen, 0) } @@ -738,7 +736,7 @@ SideInfo: snap.SideInfo{RealName: "foo", SnapID: "foo-id", Revision: snap.R(9)}, } - validated, err := assertstate.ValidateRefreshes(s.state, []*snap.Info{fooRefresh}, 0) + validated, err := assertstate.ValidateRefreshes(s.state, []*snap.Info{fooRefresh}, nil, 0) c.Assert(err, IsNil) c.Check(validated, DeepEquals, []*snap.Info{fooRefresh}) } @@ -767,11 +765,40 @@ SideInfo: snap.SideInfo{RealName: "foo", SnapID: "foo-id", Revision: snap.R(9)}, } - validated, err := assertstate.ValidateRefreshes(s.state, []*snap.Info{fooRefresh}, 0) + validated, err := assertstate.ValidateRefreshes(s.state, []*snap.Info{fooRefresh}, nil, 0) c.Assert(err, ErrorMatches, `cannot refresh "foo" to revision 9: no validation by "bar"`) c.Check(validated, HasLen, 0) } +func (s *assertMgrSuite) TestValidateRefreshesMissingValidationButIgnore(c *C) { + s.state.Lock() + defer s.state.Unlock() + + snapDeclFoo := s.snapDecl(c, "foo", nil) + snapDeclBar := s.snapDecl(c, "bar", map[string]interface{}{ + "refresh-control": []interface{}{"foo-id"}, + }) + s.stateFromDecl(snapDeclFoo, snap.R(7)) + s.stateFromDecl(snapDeclBar, snap.R(3)) + + err := assertstate.Add(s.state, s.storeSigning.StoreAccountKey("")) + c.Assert(err, IsNil) + err = assertstate.Add(s.state, s.dev1Acct) + c.Assert(err, IsNil) + err = assertstate.Add(s.state, snapDeclFoo) + c.Assert(err, IsNil) + err = assertstate.Add(s.state, snapDeclBar) + c.Assert(err, IsNil) + + fooRefresh := &snap.Info{ + SideInfo: snap.SideInfo{RealName: "foo", SnapID: "foo-id", Revision: snap.R(9)}, + } + + validated, err := assertstate.ValidateRefreshes(s.state, []*snap.Info{fooRefresh}, map[string]bool{"foo-id": true}, 0) + c.Assert(err, IsNil) + c.Check(validated, DeepEquals, []*snap.Info{fooRefresh}) +} + func (s *assertMgrSuite) TestValidateRefreshesValidationOK(c *C) { s.state.Lock() defer s.state.Unlock() @@ -835,7 +862,7 @@ SideInfo: snap.SideInfo{RealName: "foo", SnapID: "foo-id", Revision: snap.R(9)}, } - validated, err := assertstate.ValidateRefreshes(s.state, []*snap.Info{fooRefresh}, 0) + validated, err := assertstate.ValidateRefreshes(s.state, []*snap.Info{fooRefresh}, nil, 0) c.Assert(err, IsNil) c.Check(validated, DeepEquals, []*snap.Info{fooRefresh}) } @@ -904,7 +931,7 @@ SideInfo: snap.SideInfo{RealName: "foo", SnapID: "foo-id", Revision: snap.R(9)}, } - validated, err := assertstate.ValidateRefreshes(s.state, []*snap.Info{fooRefresh}, 0) + validated, err := assertstate.ValidateRefreshes(s.state, []*snap.Info{fooRefresh}, nil, 0) c.Assert(err, ErrorMatches, `(?s).*cannot refresh "foo" to revision 9: validation by "baz" \(id "baz-id"\) revoked.*`) c.Check(validated, HasLen, 0) } @@ -917,7 +944,7 @@ defer r1() baseDecl, err := assertstate.BaseDeclaration(s.state) - c.Assert(err, Equals, asserts.ErrNotFound) + c.Assert(asserts.IsNotFound(err), Equals, true) c.Check(baseDecl, IsNil) r2 := assertstest.MockBuiltinBaseDeclaration([]byte(` @@ -949,7 +976,7 @@ c.Assert(err, IsNil) _, err = assertstate.SnapDeclaration(s.state, "snap-id-other") - c.Check(err, Equals, asserts.ErrNotFound) + c.Check(asserts.IsNotFound(err), Equals, true) snapDecl, err := assertstate.SnapDeclaration(s.state, "foo-id") c.Assert(err, IsNil) @@ -978,7 +1005,7 @@ SnapID: "baz-id", }, }) - c.Check(err, ErrorMatches, `internal error: cannot find snap-declaration for installed snap "baz": assertion not found`) + c.Check(err, ErrorMatches, `internal error: cannot find snap-declaration for installed snap "baz": snap-declaration \(baz-id; series:16\) not found`) info := snaptest.MockInfo(c, ` name: foo @@ -1038,7 +1065,7 @@ SnapID: "baz-id", }, }) - c.Check(err, ErrorMatches, `internal error: cannot find snap-declaration for installed snap "baz": assertion not found`) + c.Check(err, ErrorMatches, `internal error: cannot find snap-declaration for installed snap "baz": snap-declaration \(baz-id; series:16\) not found`) // empty list // have a declaration in the system db @@ -1097,7 +1124,7 @@ c.Assert(err, IsNil) _, err = assertstate.SnapDeclaration(s.state, "snap-id-other") - c.Check(err, Equals, asserts.ErrNotFound) + c.Check(asserts.IsNotFound(err), Equals, true) acct, err := assertstate.Publisher(s.state, "foo-id") c.Assert(err, IsNil) diff -Nru snapd-2.28.5/overlord/assertstate/helpers.go snapd-2.29.3/overlord/assertstate/helpers.go --- snapd-2.28.5/overlord/assertstate/helpers.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/overlord/assertstate/helpers.go 2017-10-23 06:17:27.000000000 +0000 @@ -26,8 +26,8 @@ "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/overlord/auth" - "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" ) // TODO: snapstate also has this, move to auth, or change a bit the approach now that we have AuthContext in the store? @@ -104,7 +104,7 @@ return err } - sto := snapstate.Store(s) + sto := storestate.Store(s) retrieve := func(ref *asserts.Ref) (asserts.Assertion, error) { // TODO: ignore errors if already in db? diff -Nru snapd-2.28.5/overlord/auth/auth_test.go snapd-2.29.3/overlord/auth/auth_test.go --- snapd-2.28.5/overlord/auth/auth_test.go 2017-10-12 19:26:13.000000000 +0000 +++ snapd-2.29.3/overlord/auth/auth_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -692,6 +692,23 @@ c.Check(storeID, Equals, "env-store-id") } +func (as *authSuite) TestAuthContextWithDeviceAssertionsGenericClassicModelNoEnvVar(c *C) { + model, err := asserts.Decode([]byte(exModel)) + c.Assert(err, IsNil) + // (ab)use the example as the generic classic model + r := sysdb.MockGenericClassicModel(model.(*asserts.Model)) + defer r() + // having assertions in state + authContext := auth.NewAuthContext(as.state, &testDeviceAssertions{}) + + // for the generic classic model we continue to consider the env var + // but when the env var is unset we don't do anything wrong. + os.Unsetenv("UBUNTU_STORE_ID") + storeID, err := authContext.StoreID("store-id") + c.Assert(err, IsNil) + c.Check(storeID, Equals, "store-id") +} + func (as *authSuite) TestUsers(c *C) { as.state.Lock() user1, err1 := auth.NewUser(as.state, "user1", "email1@test.com", "macaroon", []string{"discharge"}) diff -Nru snapd-2.28.5/overlord/configstate/config/helpers.go snapd-2.29.3/overlord/configstate/config/helpers.go --- snapd-2.28.5/overlord/configstate/config/helpers.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/configstate/config/helpers.go 2017-10-23 06:17:27.000000000 +0000 @@ -34,6 +34,9 @@ var validKey = regexp.MustCompile("^(?:[a-z0-9]+-?)*[a-z](?:-?[a-z0-9])*$") func ParseKey(key string) (subkeys []string, err error) { + if key == "" { + return []string{}, nil + } subkeys = strings.Split(key, ".") for _, subkey := range subkeys { if !validKey.MatchString(subkey) { @@ -90,6 +93,18 @@ // The provided key may be formed as a dotted key path through nested maps. // For example, the "a.b.c" key describes the {a: {b: {c: value}}} map. func GetFromChange(snapName string, subkeys []string, pos int, config map[string]interface{}, result interface{}) error { + // special case - get root document + if len(subkeys) == 0 { + if config == nil { + return &NoOptionError{SnapName: snapName, Key: ""} + } + raw := jsonRaw(config) + + if err := jsonutil.DecodeWithNumber(bytes.NewReader(*raw), &result); err != nil { + return fmt.Errorf("internal error: cannot unmarshal snap %q root document: %s", snapName, err) + } + return nil + } value, ok := config[subkeys[pos]] if !ok { return &NoOptionError{SnapName: snapName, Key: strings.Join(subkeys[:pos+1], ".")} diff -Nru snapd-2.28.5/overlord/configstate/config/transaction.go snapd-2.29.3/overlord/configstate/config/transaction.go --- snapd-2.28.5/overlord/configstate/config/transaction.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/configstate/config/transaction.go 2017-10-23 06:17:27.000000000 +0000 @@ -143,6 +143,18 @@ } func getFromPristine(snapName string, subkeys []string, pos int, config map[string]*json.RawMessage, result interface{}) error { + // special case - get root document + if len(subkeys) == 0 { + if len(config) == 0 { + return &NoOptionError{SnapName: snapName} + } + raw := jsonRaw(config) + if err := jsonutil.DecodeWithNumber(bytes.NewReader(*raw), &result); err != nil { + return fmt.Errorf("internal error: cannot unmarshal snap %q root document: %s", snapName, err) + } + return nil + } + raw, ok := config[subkeys[pos]] if !ok { return &NoOptionError{SnapName: snapName, Key: strings.Join(subkeys[:pos+1], ".")} @@ -251,5 +263,8 @@ } func (e *NoOptionError) Error() string { + if e.Key == "" { + return fmt.Sprintf("snap %q has no configuration", e.SnapName) + } return fmt.Sprintf("snap %q has no %q configuration option", e.SnapName, e.Key) } diff -Nru snapd-2.28.5/overlord/configstate/config/transaction_test.go snapd-2.29.3/overlord/configstate/config/transaction_test.go --- snapd-2.28.5/overlord/configstate/config/transaction_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/configstate/config/transaction_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -103,6 +103,13 @@ `set doc={"one":1,"two":2}`, `get doc={"one":1,"two":2}`, }, { + // Root doc + `set doc={"one":1,"two":2}`, + `getroot ={"doc":{"one":1,"two":2}}`, + `commit`, + `getroot ={"doc":{"one":1,"two":2}}`, + `getrootunder ={"doc":{"one":1,"two":2}}`, +}, { // Nested mutations. `set one.two.three=3`, `set one.five=5`, @@ -248,7 +255,20 @@ c.Assert(jsonutil.DecodeWithNumber(bytes.NewReader(*obtained), &cfg), IsNil) c.Assert(cfg, DeepEquals, expected) } - + case "getroot": + var obtained interface{} + c.Assert(t.Get(snap, "", &obtained), IsNil) + c.Assert(obtained, DeepEquals, op.args()[""]) + case "getrootunder": + var config map[string]*json.RawMessage + s.state.Get("config", &config) + for _, expected := range op.args() { + obtained, ok := config[snap] + c.Assert(ok, Equals, true) + var cfg interface{} + c.Assert(jsonutil.DecodeWithNumber(bytes.NewReader(*obtained), &cfg), IsNil) + c.Assert(cfg, DeepEquals, expected) + } default: panic("unknown test op kind: " + op.kind()) } @@ -286,3 +306,15 @@ err = tr.Get("test-snap", "foo", &broken) c.Assert(err, ErrorMatches, ".*BAM!.*") } + +func (s *transactionSuite) TestNoConfiguration(c *C) { + s.state.Lock() + defer s.state.Unlock() + + var res interface{} + tr := config.NewTransaction(s.state) + err := tr.Get("some-snap", "", &res) + c.Assert(err, NotNil) + c.Assert(config.IsNoOption(err), Equals, true) + c.Assert(err, ErrorMatches, `snap "some-snap" has no configuration`) +} diff -Nru snapd-2.28.5/overlord/configstate/configstate.go snapd-2.29.3/overlord/configstate/configstate.go --- snapd-2.28.5/overlord/configstate/configstate.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/overlord/configstate/configstate.go 2017-10-23 06:17:27.000000000 +0000 @@ -26,7 +26,7 @@ "os" "time" - "github.com/snapcore/snapd/i18n/dumb" + "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/overlord/hookstate" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" @@ -36,7 +36,7 @@ snapstate.Configure = Configure } -func configureHookTimeout() time.Duration { +func ConfigureHookTimeout() time.Duration { timeout := 5 * time.Minute if s := os.Getenv("SNAPD_CONFIGURE_HOOK_TIMEOUT"); s != "" { if to, err := time.ParseDuration(s); err == nil { @@ -55,7 +55,7 @@ IgnoreError: flags&snapstate.IgnoreHookError != 0, TrackError: flags&snapstate.TrackHookError != 0, // all configure hooks must finish within this timeout - Timeout: configureHookTimeout(), + Timeout: ConfigureHookTimeout(), } var contextData map[string]interface{} if flags&snapstate.UseConfigDefaults != 0 { diff -Nru snapd-2.28.5/overlord/configstate/handler_test.go snapd-2.29.3/overlord/configstate/handler_test.go --- snapd-2.28.5/overlord/configstate/handler_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/overlord/configstate/handler_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -43,6 +43,7 @@ state *state.State context *hookstate.Context handler hookstate.Handler + restore func() } var _ = Suite(&configureHandlerSuite{}) @@ -52,6 +53,8 @@ s.state.Lock() defer s.state.Unlock() + s.restore = snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) + task := s.state.NewTask("test-task", "my test task") setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"} @@ -62,6 +65,10 @@ s.handler = configstate.NewConfigureHandler(s.context) } +func (s *configureHandlerSuite) TearDownTest(c *C) { + s.restore() +} + func (s *configureHandlerSuite) TestBeforeInitializesTransaction(c *C) { // Initialize context s.context.Lock() diff -Nru snapd-2.28.5/overlord/devicestate/devicemgr.go snapd-2.29.3/overlord/devicestate/devicemgr.go --- snapd-2.28.5/overlord/devicestate/devicemgr.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/devicestate/devicemgr.go 2017-10-23 06:17:27.000000000 +0000 @@ -28,7 +28,7 @@ "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/asserts/sysdb" "github.com/snapcore/snapd/dirs" - "github.com/snapcore/snapd/i18n/dumb" + "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/overlord/assertstate" "github.com/snapcore/snapd/overlord/auth" "github.com/snapcore/snapd/overlord/hookstate" @@ -55,6 +55,8 @@ // Manager returns a new device manager. func Manager(s *state.State, hookManager *hookstate.HookManager) (*DeviceManager, error) { + delayedCrossMgrInit() + runner := state.NewTaskRunner(s) keypairMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir) @@ -252,6 +254,9 @@ } prepareDevice = hookstate.HookTask(m.state, summary, hooksup, nil) tasks = append(tasks, prepareDevice) + // hooks are under a different manager, make sure we consider + // it immediately + m.state.EnsureBefore(0) } genKey := m.state.NewTask("generate-device-key", i18n.G("Generate device key")) diff -Nru snapd-2.28.5/overlord/devicestate/devicestate.go snapd-2.29.3/overlord/devicestate/devicestate.go --- snapd-2.28.5/overlord/devicestate/devicestate.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/devicestate/devicestate.go 2017-10-23 06:17:27.000000000 +0000 @@ -23,6 +23,7 @@ import ( "fmt" + "sync" "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/logger" @@ -50,7 +51,7 @@ "brand-id": device.Brand, "model": device.Model, }) - if err == asserts.ErrNotFound { + if asserts.IsNotFound(err) { return nil, state.ErrNoState } if err != nil { @@ -76,7 +77,7 @@ "model": device.Model, "serial": device.Serial, }) - if err == asserts.ErrNotFound { + if asserts.IsNotFound(err) { return nil, state.ErrNoState } if err != nil { @@ -188,7 +189,11 @@ return nil } -func init() { - snapstate.AddCheckSnapCallback(checkGadgetOrKernel) +var once sync.Once + +func delayedCrossMgrInit() { + once.Do(func() { + snapstate.AddCheckSnapCallback(checkGadgetOrKernel) + }) snapstate.CanAutoRefresh = canAutoRefresh } diff -Nru snapd-2.28.5/overlord/devicestate/devicestate_test.go snapd-2.29.3/overlord/devicestate/devicestate_test.go --- snapd-2.28.5/overlord/devicestate/devicestate_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/devicestate/devicestate_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -42,6 +42,7 @@ "github.com/snapcore/snapd/boot/boottest" "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/httputil" + "github.com/snapcore/snapd/overlord" "github.com/snapcore/snapd/overlord/assertstate" "github.com/snapcore/snapd/overlord/auth" "github.com/snapcore/snapd/overlord/devicestate" @@ -49,11 +50,11 @@ "github.com/snapcore/snapd/overlord/hookstate/ctlcmd" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" "github.com/snapcore/snapd/partition" "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/snap/snaptest" - "github.com/snapcore/snapd/store" "github.com/snapcore/snapd/store/storetest" "github.com/snapcore/snapd/strutil" ) @@ -61,11 +62,14 @@ func TestDeviceManager(t *testing.T) { TestingT(t) } type deviceMgrSuite struct { + o *overlord.Overlord state *state.State hookMgr *hookstate.HookManager mgr *devicestate.DeviceManager db *asserts.Database + bootloader *boottest.MockBootloader + storeSigning *assertstest.StoreStack brandSigning *assertstest.SigningDB @@ -73,6 +77,7 @@ restoreOnClassic func() restoreGenericClassicMod func() + restoreSanitize func() } var _ = Suite(&deviceMgrSuite{}) @@ -95,21 +100,23 @@ func (sto *fakeStore) Assertion(assertType *asserts.AssertionType, key []string, _ *auth.UserState) (asserts.Assertion, error) { sto.pokeStateLock() ref := &asserts.Ref{Type: assertType, PrimaryKey: key} - a, err := ref.Resolve(sto.db.Find) - if err != nil { - return nil, &store.AssertionNotFoundError{Ref: ref} - } - return a, nil + return ref.Resolve(sto.db.Find) } func (s *deviceMgrSuite) SetUpTest(c *C) { dirs.SetRootDir(c.MkDir()) os.MkdirAll(dirs.SnapRunDir, 0755) + s.restoreSanitize = snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) + + s.bootloader = boottest.NewMockBootloader("mock", c.MkDir()) + partition.ForceBootloader(s.bootloader) + s.restoreOnClassic = release.MockOnClassic(false) s.storeSigning = assertstest.NewStoreStack("canonical", nil) - s.state = state.New(nil) + s.o = overlord.Mock() + s.state = s.o.State() s.restoreGenericClassicMod = sysdb.MockGenericClassicModel(s.storeSigning.GenericClassicModel) @@ -137,10 +144,12 @@ s.db = db s.hookMgr = hookMgr + s.o.AddManager(s.hookMgr) s.mgr = mgr + s.o.AddManager(s.mgr) s.state.Lock() - snapstate.ReplaceStore(s.state, &fakeStore{ + storestate.ReplaceStore(s.state, &fakeStore{ state: s.state, db: s.storeSigning, }) @@ -151,18 +160,18 @@ s.state.Lock() assertstate.ReplaceDB(s.state, nil) s.state.Unlock() + partition.ForceBootloader(nil) dirs.SetRootDir("") s.restoreGenericClassicMod() s.restoreOnClassic() + s.restoreSanitize() } -func (s *deviceMgrSuite) settle() { - for i := 0; i < 50; i++ { - s.hookMgr.Ensure() - s.mgr.Ensure() - s.hookMgr.Wait() - s.mgr.Wait() - } +var settleTimeout = 15 * time.Second + +func (s *deviceMgrSuite) settle(c *C) { + err := s.o.Settle(settleTimeout) + c.Assert(err, IsNil) } const ( @@ -356,7 +365,7 @@ // runs the whole device registration process s.state.Unlock() - s.settle() + s.settle(c) s.state.Lock() becomeOperational = s.findBecomeOperationalChange() @@ -424,7 +433,7 @@ // runs the whole device registration process s.state.Unlock() - s.settle() + s.settle(c) s.state.Lock() becomeOperational := s.findBecomeOperationalChange() @@ -495,7 +504,7 @@ // runs the whole device registration process s.state.Unlock() - s.settle() + s.settle(c) s.state.Lock() becomeOperational = s.findBecomeOperationalChange() @@ -581,7 +590,7 @@ // runs the whole device registration process s.state.Unlock() - s.settle() + s.settle(c) s.state.Lock() becomeOperational := s.findBecomeOperationalChange() @@ -735,7 +744,7 @@ "model": "pc", "serial": "9999", }) - c.Assert(err, Equals, asserts.ErrNotFound) + c.Assert(asserts.IsNotFound(err), Equals, true) s.state.Unlock() s.mgr.Ensure() @@ -795,12 +804,19 @@ // runs the whole device registration process with polling s.state.Unlock() - s.settle() + s.settle(c) s.state.Lock() becomeOperational := s.findBecomeOperationalChange() c.Assert(becomeOperational, NotNil) + // needs 3 more Retry passes of polling + for i := 0; i < 3; i++ { + s.state.Unlock() + s.settle(c) + s.state.Lock() + } + c.Check(becomeOperational.Status().Ready(), Equals, true) c.Check(becomeOperational.Err(), IsNil) @@ -890,7 +906,7 @@ // runs the whole device registration process s.state.Unlock() - s.settle() + s.settle(c) s.state.Lock() becomeOperational := s.findBecomeOperationalChange() @@ -973,7 +989,7 @@ // try the whole device registration process s.state.Unlock() - s.settle() + s.settle(c) s.state.Lock() becomeOperational := s.findBecomeOperationalChange() @@ -996,7 +1012,7 @@ s.reqID = "REQID-1" s.mgr.SetLastBecomeOperationalAttempt(time.Now().Add(-15 * time.Minute)) s.state.Unlock() - s.settle() + s.settle(c) s.state.Lock() becomeOperational = s.findBecomeOperationalChange(firstTryID) @@ -1078,7 +1094,7 @@ // try the whole device registration process s.state.Unlock() - s.settle() + s.settle(c) s.state.Lock() becomeOperational := s.findBecomeOperationalChange() @@ -1317,10 +1333,7 @@ } func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkBootloaderHappy(c *C) { - bootloader := boottest.NewMockBootloader("mock", c.MkDir()) - partition.ForceBootloader(bootloader) - defer partition.ForceBootloader(nil) - bootloader.SetBootVars(map[string]string{ + s.bootloader.SetBootVars(map[string]string{ "snap_mode": "trying", "snap_try_core": "core_1.snap", }) @@ -1340,18 +1353,14 @@ s.state.Lock() c.Assert(err, IsNil) - m, err := bootloader.GetBootVars("snap_mode") + m, err := s.bootloader.GetBootVars("snap_mode") c.Assert(err, IsNil) c.Assert(m, DeepEquals, map[string]string{"snap_mode": ""}) } func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkUpdateBootRevisionsHappy(c *C) { - bootloader := boottest.NewMockBootloader("mock", c.MkDir()) - partition.ForceBootloader(bootloader) - defer partition.ForceBootloader(nil) - // simulate that we have a new core_2, tried to boot it but that failed - bootloader.SetBootVars(map[string]string{ + s.bootloader.SetBootVars(map[string]string{ "snap_mode": "", "snap_try_core": "core_2.snap", "snap_core": "core_1.snap", @@ -1386,14 +1395,11 @@ } func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkNotRunAgain(c *C) { - bootloader := boottest.NewMockBootloader("mock", c.MkDir()) - bootloader.SetBootVars(map[string]string{ + s.bootloader.SetBootVars(map[string]string{ "snap_mode": "trying", "snap_try_core": "core_1.snap", }) - bootloader.SetErr = fmt.Errorf("ensure bootloader is not used") - partition.ForceBootloader(bootloader) - defer partition.ForceBootloader(nil) + s.bootloader.SetErr = fmt.Errorf("ensure bootloader is not used") s.mgr.SetBootOkRan(true) @@ -1413,10 +1419,7 @@ }) s.state.Unlock() - bootloader := boottest.NewMockBootloader("mock", c.MkDir()) - bootloader.GetErr = fmt.Errorf("bootloader err") - partition.ForceBootloader(bootloader) - defer partition.ForceBootloader(nil) + s.bootloader.GetErr = fmt.Errorf("bootloader err") s.mgr.SetBootOkRan(false) diff -Nru snapd-2.28.5/overlord/devicestate/firstboot.go snapd-2.29.3/overlord/devicestate/firstboot.go --- snapd-2.28.5/overlord/devicestate/firstboot.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/overlord/devicestate/firstboot.go 2017-10-23 06:17:27.000000000 +0000 @@ -29,7 +29,7 @@ "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/asserts/snapasserts" "github.com/snapcore/snapd/dirs" - "github.com/snapcore/snapd/i18n/dumb" + "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/overlord/assertstate" "github.com/snapcore/snapd/overlord/auth" @@ -56,7 +56,7 @@ sideInfo.RealName = sn.Name } else { si, err := snapasserts.DeriveSideInfo(path, assertstate.DB(st)) - if err == asserts.ErrNotFound { + if asserts.IsNotFound(err) { return nil, fmt.Errorf("cannot find signatures with metadata for snap %q (%q)", sn.Name, path) } if err != nil { diff -Nru snapd-2.28.5/overlord/devicestate/firstboot_test.go snapd-2.29.3/overlord/devicestate/firstboot_test.go --- snapd-2.28.5/overlord/devicestate/firstboot_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/devicestate/firstboot_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -43,6 +43,7 @@ "github.com/snapcore/snapd/overlord/configstate/config" "github.com/snapcore/snapd/overlord/devicestate" "github.com/snapcore/snapd/overlord/hookstate" + "github.com/snapcore/snapd/overlord/ifacestate" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" "github.com/snapcore/snapd/partition" @@ -106,6 +107,10 @@ ovld, err := overlord.New() c.Assert(err, IsNil) s.overlord = ovld + + // don't actually try to talk to the store on snapstate.Ensure + // needs doing after the call to devicestate.Manager (which happens in overlord.New) + snapstate.CanAutoRefresh = nil } func (s *FirstBootTestSuite) TearDownTest(c *C) { @@ -275,7 +280,7 @@ return coreFname, kernelFname, gadgetFname } -func (s *FirstBootTestSuite) makeBecomeOpertionalChange(c *C) *state.Change { +func (s *FirstBootTestSuite) makeBecomeOperationalChange(c *C, st *state.State) *state.Change { coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, false) devAcct := assertstest.NewAccount(s.storeSigning, "developer", map[string]interface{}{ @@ -335,7 +340,6 @@ c.Assert(err, IsNil) // run the firstboot stuff - st := s.overlord.State() st.Lock() defer st.Unlock() tsAll, err := devicestate.PopulateStateFromSeedImpl(st) @@ -361,11 +365,6 @@ chg1 := st.NewChange("become-operational", "init device") chg1.SetStatus(state.DoingStatus) - st.Unlock() - s.overlord.Settle() - st.Lock() - // unlocked by defer - return chg } @@ -378,8 +377,11 @@ "snap_kernel": "pc-kernel_1.snap", }) - chg := s.makeBecomeOpertionalChange(c) st := s.overlord.State() + chg := s.makeBecomeOperationalChange(c, st) + err := s.overlord.Settle(settleTimeout) + c.Assert(err, IsNil) + st.Lock() defer st.Unlock() @@ -441,11 +443,39 @@ } func (s *FirstBootTestSuite) TestPopulateFromSeedMissingBootloader(c *C) { - chg := s.makeBecomeOpertionalChange(c) - st := s.overlord.State() + st0 := s.overlord.State() + st0.Lock() + db := assertstate.DB(st0) + st0.Unlock() + + // we run only with the relevant managers to produce the error + // situation + o := overlord.Mock() + st := o.State() + snapmgr, err := snapstate.Manager(st) + c.Assert(err, IsNil) + o.AddManager(snapmgr) + + ifacemgr, err := ifacestate.Manager(st, nil, nil, nil) + c.Assert(err, IsNil) + o.AddManager(ifacemgr) st.Lock() - defer st.Unlock() + assertstate.ReplaceDB(st, db.(*asserts.Database)) + st.Unlock() + + chg := s.makeBecomeOperationalChange(c, st) + + // we cannot use Settle because the Change will not become Clean + // under the subset of managers + for i := 0; i < 25 && !chg.IsReady(); i++ { + snapmgr.Ensure() + ifacemgr.Ensure() + snapmgr.Wait() + ifacemgr.Wait() + } + st.Lock() + defer st.Unlock() c.Assert(chg.Err(), ErrorMatches, `(?s).* cannot determine bootloader.*`) } @@ -534,9 +564,10 @@ chg1.SetStatus(state.DoingStatus) st.Unlock() - s.overlord.Settle() + err = s.overlord.Settle(settleTimeout) st.Lock() c.Assert(chg.Err(), IsNil) + c.Assert(err, IsNil) // and check the snap got correctly installed c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true) @@ -717,9 +748,10 @@ chg1.SetStatus(state.DoingStatus) st.Unlock() - s.overlord.Settle() + err = s.overlord.Settle(settleTimeout) st.Lock() c.Assert(chg.Err(), IsNil) + c.Assert(err, IsNil) // and check the snap got correctly installed c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true) @@ -881,7 +913,7 @@ // try import and verify that its rejects because other assertions are // missing _, err := devicestate.ImportAssertionsFromSeed(st) - c.Assert(err, ErrorMatches, "cannot find account-key .*: assertion not found") + c.Assert(err, ErrorMatches, "cannot find account-key .*") } func (s *FirstBootTestSuite) TestImportAssertionsFromSeedTwoModelAsserts(c *C) { diff -Nru snapd-2.28.5/overlord/devicestate/handlers.go snapd-2.29.3/overlord/devicestate/handlers.go --- snapd-2.28.5/overlord/devicestate/handlers.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/devicestate/handlers.go 2017-10-23 06:17:27.000000000 +0000 @@ -37,6 +37,7 @@ "github.com/snapcore/snapd/overlord/configstate/config" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" ) func (m *DeviceManager) doMarkSeeded(t *state.Task, _ *tomb.Tomb) error { @@ -429,7 +430,7 @@ "model": device.Model, "device-key-sha3-384": privKey.PublicKey().ID(), }) - if err != nil && err != asserts.ErrNotFound { + if err != nil && !asserts.IsNotFound(err) { return err } @@ -490,7 +491,7 @@ var repeatRequestSerial string // for tests func fetchKeys(st *state.State, keyID string) (errAcctKey error, err error) { - sto := snapstate.Store(st) + sto := storestate.Store(st) db := assertstate.DB(st) for { _, err := db.FindPredefined(asserts.AccountKeyType, map[string]string{ @@ -499,7 +500,7 @@ if err == nil { return nil, nil } - if err != asserts.ErrNotFound { + if !asserts.IsNotFound(err) { return nil, err } st.Unlock() diff -Nru snapd-2.28.5/overlord/export_test.go snapd-2.29.3/overlord/export_test.go --- snapd-2.28.5/overlord/export_test.go 2016-08-29 15:03:59.000000000 +0000 +++ snapd-2.29.3/overlord/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -23,7 +23,8 @@ "time" "github.com/snapcore/snapd/overlord/auth" - "github.com/snapcore/snapd/store" + "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" ) // MockEnsureInterval sets the overlord ensure interval for tests. @@ -58,10 +59,10 @@ return o.stateEng } -// MockStoreNew mocks store.New as called by overlord.New. -func MockStoreNew(new func(*store.Config, auth.AuthContext) *store.Store) (restore func()) { - storeNew = new +// MockSetupStore mocks storestate.SetupStore as called by overlord.New. +func MockSetupStore(new func(*state.State, auth.AuthContext) error) (restore func()) { + setupStore = new return func() { - storeNew = store.New + setupStore = storestate.SetupStore } } diff -Nru snapd-2.28.5/overlord/hookstate/context.go snapd-2.29.3/overlord/hookstate/context.go --- snapd-2.28.5/overlord/hookstate/context.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/hookstate/context.go 2017-10-27 12:23:38.000000000 +0000 @@ -80,6 +80,12 @@ return c.setup.Revision } +// Task returns the task associated with the hook or (nil, false) if the context is ephemeral +// and task is not available. +func (c *Context) Task() (*state.Task, bool) { + return c.task, c.task != nil +} + // HookName returns the name of the hook in this context. func (c *Context) HookName() string { return c.setup.Hook diff -Nru snapd-2.28.5/overlord/hookstate/ctlcmd/export_test.go snapd-2.29.3/overlord/hookstate/ctlcmd/export_test.go --- snapd-2.28.5/overlord/hookstate/ctlcmd/export_test.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/overlord/hookstate/ctlcmd/export_test.go 2017-10-27 12:23:38.000000000 +0000 @@ -19,11 +19,22 @@ package ctlcmd -import "fmt" +import ( + "fmt" + "github.com/snapcore/snapd/overlord/servicestate" + "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/snap" +) var AttributesTask = attributesTask var CopyAttributes = copyAttributes +func MockServicestateControlFunc(f func(*state.State, []*snap.AppInfo, *servicestate.Instruction) (*state.TaskSet, error)) (restore func()) { + old := servicestateControl + servicestateControl = f + return func() { servicestateControl = old } +} + func AddMockCommand(name string) *MockCommand { mockCommand := NewMockCommand() addCommand(name, "", "", func() command { return mockCommand }) diff -Nru snapd-2.28.5/overlord/hookstate/ctlcmd/get.go snapd-2.29.3/overlord/hookstate/ctlcmd/get.go --- snapd-2.28.5/overlord/hookstate/ctlcmd/get.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/overlord/hookstate/ctlcmd/get.go 2017-10-23 06:17:27.000000000 +0000 @@ -24,7 +24,7 @@ "fmt" "strings" - "github.com/snapcore/snapd/i18n/dumb" + "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/overlord/configstate" "github.com/snapcore/snapd/overlord/configstate/config" diff -Nru snapd-2.28.5/overlord/hookstate/ctlcmd/helpers.go snapd-2.29.3/overlord/hookstate/ctlcmd/helpers.go --- snapd-2.28.5/overlord/hookstate/ctlcmd/helpers.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/overlord/hookstate/ctlcmd/helpers.go 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,123 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package ctlcmd + +import ( + "fmt" + "strings" + "time" + + "github.com/snapcore/snapd/i18n" + "github.com/snapcore/snapd/overlord/configstate" + "github.com/snapcore/snapd/overlord/hookstate" + "github.com/snapcore/snapd/overlord/servicestate" + "github.com/snapcore/snapd/overlord/snapstate" + "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/snap" +) + +func getServiceInfos(st *state.State, snapName string, serviceNames []string) ([]*snap.AppInfo, error) { + st.Lock() + defer st.Unlock() + + var snapst snapstate.SnapState + if err := snapstate.Get(st, snapName, &snapst); err != nil { + return nil, err + } + + info, err := snapst.CurrentInfo() + if err != nil { + return nil, err + } + + var svcs []*snap.AppInfo + for _, svcName := range serviceNames { + if svcName == snapName { + // all the services + return info.Services(), nil + } + if !strings.HasPrefix(svcName, snapName+".") { + return nil, fmt.Errorf(i18n.G("unknown service: %q"), svcName) + } + // this doesn't support service aliases + app, ok := info.Apps[svcName[1+len(snapName):]] + if !(ok && app.IsService()) { + return nil, fmt.Errorf(i18n.G("unknown service: %q"), svcName) + } + svcs = append(svcs, app) + } + + return svcs, nil +} + +var servicestateControl = servicestate.Control + +func queueCommand(context *hookstate.Context, ts *state.TaskSet) { + // queue command task after all existing tasks of the hook's change + st := context.State() + st.Lock() + defer st.Unlock() + + task, ok := context.Task() + if !ok { + panic("attempted to queue command with ephemeral context") + } + change := task.Change() + tasks := change.Tasks() + ts.WaitAll(state.NewTaskSet(tasks...)) + change.AddAll(ts) +} + +func runServiceCommand(context *hookstate.Context, inst *servicestate.Instruction, serviceNames []string) error { + if context == nil { + return fmt.Errorf(i18n.G("cannot %s without a context"), inst.Action) + } + + st := context.State() + appInfos, err := getServiceInfos(st, context.SnapName(), serviceNames) + if err != nil { + return err + } + + ts, err := servicestateControl(st, appInfos, inst) + if err != nil { + return err + } + + if !context.IsEphemeral() && context.HookName() == "configure" { + queueCommand(context, ts) + return nil + } + + st.Lock() + chg := st.NewChange("service-control", fmt.Sprintf("Running service command for snap %q", context.SnapName())) + chg.AddAll(ts) + st.EnsureBefore(0) + st.Unlock() + + select { + case <-chg.Ready(): + st.Lock() + defer st.Unlock() + return chg.Err() + case <-time.After(configstate.ConfigureHookTimeout() / 2): + return fmt.Errorf("%s command is taking too long", inst.Action) + } +} diff -Nru snapd-2.28.5/overlord/hookstate/ctlcmd/restart.go snapd-2.29.3/overlord/hookstate/ctlcmd/restart.go --- snapd-2.28.5/overlord/hookstate/ctlcmd/restart.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/overlord/hookstate/ctlcmd/restart.go 2017-11-03 16:15:11.000000000 +0000 @@ -0,0 +1,57 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2016-2017 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 . + * + */ + +package ctlcmd + +import ( + "github.com/snapcore/snapd/client" + "github.com/snapcore/snapd/i18n" + "github.com/snapcore/snapd/overlord/servicestate" +) + +var ( + shortRestartHelp = i18n.G("Restart services") + longRestartHelp = i18n.G(` +The restart command restarts the given services of the snap. If executed from the +"configure" hook, the services will be restarted after the hook finishes.`) +) + +func init() { + // FIXME: uncomment once the feature is fixed to work on install/refresh + // addCommand("restart", shortRestartHelp, longRestartHelp, func() command { return &restartCommand{} }) +} + +type restartCommand struct { + baseCommand + Positional struct { + ServiceNames []string `positional-arg-name:"" required:"yes"` + } `positional-args:"yes" required:"yes"` + Reload bool `long:"reload" description:"Reload the given services if they support it (see man systemctl for details)"` +} + +func (c *restartCommand) Execute(args []string) error { + inst := servicestate.Instruction{ + Action: "restart", + Names: c.Positional.ServiceNames, + RestartOptions: client.RestartOptions{ + Reload: c.Reload, + }, + } + return runServiceCommand(c.context(), &inst, c.Positional.ServiceNames) +} diff -Nru snapd-2.28.5/overlord/hookstate/ctlcmd/services_test.go snapd-2.29.3/overlord/hookstate/ctlcmd/services_test.go --- snapd-2.28.5/overlord/hookstate/ctlcmd/services_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/overlord/hookstate/ctlcmd/services_test.go 2017-11-03 16:15:11.000000000 +0000 @@ -0,0 +1,259 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package ctlcmd_test + +import ( + "fmt" + . "gopkg.in/check.v1" + + "github.com/snapcore/snapd/client" + "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/overlord/configstate" + "github.com/snapcore/snapd/overlord/hookstate" + "github.com/snapcore/snapd/overlord/hookstate/ctlcmd" + "github.com/snapcore/snapd/overlord/hookstate/hooktest" + "github.com/snapcore/snapd/overlord/servicestate" + "github.com/snapcore/snapd/overlord/snapstate" + "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/snap" + "github.com/snapcore/snapd/snap/snaptest" + "github.com/snapcore/snapd/testutil" +) + +type servicectlSuite struct { + testutil.BaseTest + st *state.State + mockContext *hookstate.Context + mockHandler *hooktest.MockHandler +} + +var _ = Suite(&servicectlSuite{}) + +const testSnapYaml = `name: test-snap +version: 1.0 +summary: test-snap +apps: + normal-app: + command: bin/dummy + test-service: + command: bin/service + daemon: simple + reload-command: bin/reload +` + +const otherSnapYaml = `name: other-snap +version: 1.0 +summary: other-snap +apps: + test-service: + command: bin/service + daemon: simple + reload-command: bin/reload +` + +func mockServiceChangeFunc(testServiceControlInputs func(appInfos []*snap.AppInfo, inst *servicestate.Instruction)) func() { + return ctlcmd.MockServicestateControlFunc(func(st *state.State, appInfos []*snap.AppInfo, inst *servicestate.Instruction) (*state.TaskSet, error) { + testServiceControlInputs(appInfos, inst) + return nil, fmt.Errorf("forced error") + }) +} + +func (s *servicectlSuite) SetUpTest(c *C) { + c.Skip("disabled until snapctl start/stop/restart commands are restored") + + s.BaseTest.SetUpTest(c) + oldRoot := dirs.GlobalRootDir + dirs.SetRootDir(c.MkDir()) + + testutil.MockCommand(c, "systemctl", "") + + s.BaseTest.AddCleanup(func() { + dirs.SetRootDir(oldRoot) + }) + s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) + + s.mockHandler = hooktest.NewMockHandler() + + s.st = state.New(nil) + s.st.Lock() + defer s.st.Unlock() + + // mock installed snaps + info1 := snaptest.MockSnap(c, string(testSnapYaml), "", &snap.SideInfo{ + Revision: snap.R(1), + }) + info2 := snaptest.MockSnap(c, string(otherSnapYaml), "", &snap.SideInfo{ + Revision: snap.R(1), + }) + snapstate.Set(s.st, info1.Name(), &snapstate.SnapState{ + Active: true, + Sequence: []*snap.SideInfo{ + { + RealName: info1.Name(), + Revision: info1.Revision, + SnapID: "test-snap-id", + }, + }, + Current: info1.Revision, + }) + snapstate.Set(s.st, info2.Name(), &snapstate.SnapState{ + Active: true, + Sequence: []*snap.SideInfo{ + { + RealName: info2.Name(), + Revision: info2.Revision, + SnapID: "other-snap-id", + }, + }, + Current: info2.Revision, + }) + + task := s.st.NewTask("test-task", "my test task") + setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"} + + var err error + s.mockContext, err = hookstate.NewContext(task, task.State(), setup, s.mockHandler, "") + c.Assert(err, IsNil) +} + +func (s *servicectlSuite) TearDownTest(c *C) { + s.BaseTest.TearDownTest(c) +} + +func (s *servicectlSuite) TestStopCommand(c *C) { + var serviceChangeFuncCalled bool + restore := mockServiceChangeFunc(func(appInfos []*snap.AppInfo, inst *servicestate.Instruction) { + serviceChangeFuncCalled = true + c.Assert(appInfos, HasLen, 1) + c.Assert(appInfos[0].Name, Equals, "test-service") + c.Assert(inst, DeepEquals, &servicestate.Instruction{ + Action: "stop", + Names: []string{"test-snap.test-service"}, + StopOptions: client.StopOptions{ + Disable: false, + }, + }, + ) + }) + defer restore() + _, _, err := ctlcmd.Run(s.mockContext, []string{"stop", "test-snap.test-service"}) + c.Assert(err, NotNil) + c.Check(err, ErrorMatches, "forced error") + c.Assert(serviceChangeFuncCalled, Equals, true) +} + +func (s *servicectlSuite) TestStopCommandUnknownService(c *C) { + var serviceChangeFuncCalled bool + restore := mockServiceChangeFunc(func(appInfos []*snap.AppInfo, inst *servicestate.Instruction) { + serviceChangeFuncCalled = true + }) + defer restore() + _, _, err := ctlcmd.Run(s.mockContext, []string{"stop", "test-snap.fooservice"}) + c.Assert(err, NotNil) + c.Assert(err, ErrorMatches, `unknown service: "test-snap.fooservice"`) + c.Assert(serviceChangeFuncCalled, Equals, false) +} + +func (s *servicectlSuite) TestStopCommandFailsOnOtherSnap(c *C) { + var serviceChangeFuncCalled bool + restore := mockServiceChangeFunc(func(appInfos []*snap.AppInfo, inst *servicestate.Instruction) { + serviceChangeFuncCalled = true + }) + defer restore() + // verify that snapctl is not allowed to control services of other snaps (only the one of its hook) + _, _, err := ctlcmd.Run(s.mockContext, []string{"stop", "other-snap.test-service"}) + c.Check(err, NotNil) + c.Assert(err, ErrorMatches, `unknown service: "other-snap.test-service"`) + c.Assert(serviceChangeFuncCalled, Equals, false) +} + +func (s *servicectlSuite) TestStartCommand(c *C) { + var serviceChangeFuncCalled bool + restore := mockServiceChangeFunc(func(appInfos []*snap.AppInfo, inst *servicestate.Instruction) { + serviceChangeFuncCalled = true + c.Assert(appInfos, HasLen, 1) + c.Assert(appInfos[0].Name, Equals, "test-service") + c.Assert(inst, DeepEquals, &servicestate.Instruction{ + Action: "start", + Names: []string{"test-snap.test-service"}, + StartOptions: client.StartOptions{ + Enable: false, + }, + }, + ) + }) + defer restore() + _, _, err := ctlcmd.Run(s.mockContext, []string{"start", "test-snap.test-service"}) + c.Check(err, NotNil) + c.Check(err, ErrorMatches, "forced error") + c.Assert(serviceChangeFuncCalled, Equals, true) +} + +func (s *servicectlSuite) TestRestartCommand(c *C) { + var serviceChangeFuncCalled bool + restore := mockServiceChangeFunc(func(appInfos []*snap.AppInfo, inst *servicestate.Instruction) { + serviceChangeFuncCalled = true + c.Assert(appInfos, HasLen, 1) + c.Assert(appInfos[0].Name, Equals, "test-service") + c.Assert(inst, DeepEquals, &servicestate.Instruction{ + Action: "restart", + Names: []string{"test-snap.test-service"}, + RestartOptions: client.RestartOptions{ + Reload: false, + }, + }, + ) + }) + defer restore() + _, _, err := ctlcmd.Run(s.mockContext, []string{"restart", "test-snap.test-service"}) + c.Check(err, NotNil) + c.Check(err, ErrorMatches, "forced error") + c.Assert(serviceChangeFuncCalled, Equals, true) +} + +func (s *servicectlSuite) TestQueuedCommands(c *C) { + s.st.Lock() + ts := configstate.Configure(s.st, "test-snap", nil, 0) + chg := s.st.NewChange("configure change", "configure change") + chg.AddAll(ts) + s.st.Unlock() + + task := ts.Tasks()[0] + setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "configure"} + context, err := hookstate.NewContext(task, task.State(), setup, s.mockHandler, "") + c.Assert(err, IsNil) + + _, _, err = ctlcmd.Run(context, []string{"stop", "test-snap.test-service"}) + c.Check(err, IsNil) + _, _, err = ctlcmd.Run(context, []string{"start", "test-snap.test-service"}) + c.Check(err, IsNil) + _, _, err = ctlcmd.Run(context, []string{"restart", "test-snap.test-service"}) + c.Check(err, IsNil) + + s.st.Lock() + defer s.st.Unlock() + + allTasks := chg.Tasks() + c.Assert(allTasks, HasLen, 4) + c.Check(allTasks[0].Summary(), Equals, `Run configure hook of "test-snap" snap if present`) + c.Check(allTasks[1].Summary(), Equals, "stop of [test-snap.test-service]") + c.Check(allTasks[2].Summary(), Equals, "start of [test-snap.test-service]") + c.Check(allTasks[3].Summary(), Equals, "restart of [test-snap.test-service]") +} diff -Nru snapd-2.28.5/overlord/hookstate/ctlcmd/set.go snapd-2.29.3/overlord/hookstate/ctlcmd/set.go --- snapd-2.28.5/overlord/hookstate/ctlcmd/set.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/hookstate/ctlcmd/set.go 2017-10-23 06:17:27.000000000 +0000 @@ -23,7 +23,7 @@ "fmt" "strings" - "github.com/snapcore/snapd/i18n/dumb" + "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/jsonutil" "github.com/snapcore/snapd/overlord/configstate" "github.com/snapcore/snapd/overlord/hookstate" diff -Nru snapd-2.28.5/overlord/hookstate/ctlcmd/start.go snapd-2.29.3/overlord/hookstate/ctlcmd/start.go --- snapd-2.28.5/overlord/hookstate/ctlcmd/start.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/overlord/hookstate/ctlcmd/start.go 2017-11-03 16:15:11.000000000 +0000 @@ -0,0 +1,57 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2016-2017 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 . + * + */ + +package ctlcmd + +import ( + "github.com/snapcore/snapd/client" + "github.com/snapcore/snapd/i18n" + "github.com/snapcore/snapd/overlord/servicestate" +) + +var ( + shortStartHelp = i18n.G("Start services") + longStartHelp = i18n.G(` +The start command starts the given services of the snap. If executed from the +"configure" hook, the services will be started after the hook finishes.`) +) + +func init() { + // FIXME: uncomment once the feature is fixed to work on install/refresh + // addCommand("start", shortStartHelp, longStartHelp, func() command { return &startCommand{} }) +} + +type startCommand struct { + baseCommand + Positional struct { + ServiceNames []string `positional-arg-name:"" required:"yes"` + } `positional-args:"yes" required:"yes"` + Enable bool `long:"enable" description:"Enable the specified services (see man systemctl for details)"` +} + +func (c *startCommand) Execute(args []string) error { + inst := servicestate.Instruction{ + Action: "start", + Names: c.Positional.ServiceNames, + StartOptions: client.StartOptions{ + Enable: c.Enable, + }, + } + return runServiceCommand(c.context(), &inst, c.Positional.ServiceNames) +} diff -Nru snapd-2.28.5/overlord/hookstate/ctlcmd/stop.go snapd-2.29.3/overlord/hookstate/ctlcmd/stop.go --- snapd-2.28.5/overlord/hookstate/ctlcmd/stop.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/overlord/hookstate/ctlcmd/stop.go 2017-11-03 16:15:11.000000000 +0000 @@ -0,0 +1,57 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2016-2017 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 . + * + */ + +package ctlcmd + +import ( + "github.com/snapcore/snapd/client" + "github.com/snapcore/snapd/i18n" + "github.com/snapcore/snapd/overlord/servicestate" +) + +type stopCommand struct { + baseCommand + Positional struct { + ServiceNames []string `positional-arg-name:"" required:"yes"` + } `positional-args:"yes" required:"yes"` + Disable bool `long:"disable" description:"Disable the specified services (see man systemctl for details)"` +} + +var ( + shortStopHelp = i18n.G("Stop services") + longStopHelp = i18n.G(` +The stop command stops the given services of the snap. If executed from the +"configure" hook, the services will be stopped after the hook finishes.`) +) + +func init() { + // FIXME: uncomment once the feature is fixed to work on install/refresh + // addCommand("stop", shortStopHelp, longStopHelp, func() command { return &stopCommand{} }) +} + +func (c *stopCommand) Execute(args []string) error { + inst := servicestate.Instruction{ + Action: "stop", + Names: c.Positional.ServiceNames, + StopOptions: client.StopOptions{ + Disable: c.Disable, + }, + } + return runServiceCommand(c.context(), &inst, c.Positional.ServiceNames) +} diff -Nru snapd-2.28.5/overlord/hookstate/hooks.go snapd-2.29.3/overlord/hookstate/hooks.go --- snapd-2.28.5/overlord/hookstate/hooks.go 2017-10-04 14:43:22.000000000 +0000 +++ snapd-2.29.3/overlord/hookstate/hooks.go 2017-10-23 06:17:27.000000000 +0000 @@ -21,7 +21,7 @@ "fmt" "regexp" - "github.com/snapcore/snapd/i18n/dumb" + "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" ) diff -Nru snapd-2.28.5/overlord/hookstate/hookstate_test.go snapd-2.29.3/overlord/hookstate/hookstate_test.go --- snapd-2.28.5/overlord/hookstate/hookstate_test.go 2017-08-30 09:16:36.000000000 +0000 +++ snapd-2.29.3/overlord/hookstate/hookstate_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -32,6 +32,7 @@ . "gopkg.in/check.v1" "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/overlord" "github.com/snapcore/snapd/overlord/hookstate" "github.com/snapcore/snapd/overlord/hookstate/hooktest" "github.com/snapcore/snapd/overlord/snapstate" @@ -46,6 +47,7 @@ type hookManagerSuite struct { testutil.BaseTest + o *overlord.Overlord state *state.State manager *hookstate.HookManager context *hookstate.Context @@ -85,10 +87,14 @@ s.BaseTest.SetUpTest(c) dirs.SetRootDir(c.MkDir()) - s.state = state.New(nil) + s.o = overlord.Mock() + s.state = s.o.State() manager, err := hookstate.Manager(s.state) c.Assert(err, IsNil) s.manager = manager + s.o.AddManager(s.manager) + + s.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) hooksup := &hookstate.HookSetup{ Snap: "test-snap", @@ -137,11 +143,9 @@ dirs.SetRootDir("") } -func (s *hookManagerSuite) settle() { - for i := 0; i < 50; i++ { - s.manager.Ensure() - s.manager.Wait() - } +func (s *hookManagerSuite) settle(c *C) { + err := s.o.Settle(5 * time.Second) + c.Assert(err, IsNil) } func (s *hookManagerSuite) TestSmoke(c *C) { @@ -726,7 +730,7 @@ } s.state.Unlock() - s.settle() + s.settle(c) s.state.Lock() defer s.state.Unlock() @@ -828,7 +832,7 @@ s.state.Unlock() - s.settle() + s.settle(c) s.state.Lock() defer s.state.Unlock() diff -Nru snapd-2.28.5/overlord/ifacestate/handlers.go snapd-2.29.3/overlord/ifacestate/handlers.go --- snapd-2.28.5/overlord/ifacestate/handlers.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/overlord/ifacestate/handlers.go 2017-10-23 06:17:27.000000000 +0000 @@ -148,12 +148,12 @@ return err } if err := m.repo.AddSnap(snapInfo); err != nil { - if _, ok := err.(*interfaces.BadInterfacesError); ok { - task.Logf("%s", err) - } else { - return err - } + return err + } + if len(snapInfo.BadInterfaces) > 0 { + task.Logf("%s", snap.BadInterfacesSummary(snapInfo)) } + if err := m.reloadConnections(snapName); err != nil { return err } diff -Nru snapd-2.28.5/overlord/ifacestate/helpers.go snapd-2.29.3/overlord/ifacestate/helpers.go --- snapd-2.28.5/overlord/ifacestate/helpers.go 2017-10-13 18:02:58.000000000 +0000 +++ snapd-2.29.3/overlord/ifacestate/helpers.go 2017-10-23 06:17:27.000000000 +0000 @@ -138,12 +138,8 @@ // For each backend: for _, backend := range securityBackends { - // The issue this is attempting to fix is only - // affecting seccomp/apparmor so limit the work just to - // this backend. - shouldRefresh := (backend.Name() == interfaces.SecuritySecComp || backend.Name() == interfaces.SecurityAppArmor || backend.Name() == interfaces.SecurityUDev) - if !shouldRefresh { - continue + if backend.Name() == "" { + continue // Test backends have no name, skip them to simplify testing. } // Refresh security of this snap and backend if err := backend.Setup(snapInfo, opts, m.repo); err != nil { diff -Nru snapd-2.28.5/overlord/ifacestate/ifacemgr.go snapd-2.29.3/overlord/ifacestate/ifacemgr.go --- snapd-2.28.5/overlord/ifacestate/ifacemgr.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/overlord/ifacestate/ifacemgr.go 2017-10-23 06:17:27.000000000 +0000 @@ -38,6 +38,8 @@ // Manager returns a new InterfaceManager. // Extra interfaces can be provided for testing. func Manager(s *state.State, hookManager *hookstate.HookManager, extraInterfaces []interfaces.Interface, extraBackends []interfaces.SecurityBackend) (*InterfaceManager, error) { + delayedCrossMgrInit() + // NOTE: hookManager is nil only when testing. if hookManager != nil { setupHooks(hookManager) @@ -49,6 +51,7 @@ runner: runner, repo: interfaces.NewRepository(), } + if err := m.initialize(extraInterfaces, extraBackends); err != nil { return nil, err } diff -Nru snapd-2.28.5/overlord/ifacestate/ifacestate.go snapd-2.29.3/overlord/ifacestate/ifacestate.go --- snapd-2.28.5/overlord/ifacestate/ifacestate.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/overlord/ifacestate/ifacestate.go 2017-10-23 06:17:27.000000000 +0000 @@ -23,8 +23,9 @@ import ( "fmt" + "sync" - "github.com/snapcore/snapd/i18n/dumb" + "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/policy" "github.com/snapcore/snapd/overlord/assertstate" @@ -201,9 +202,13 @@ return ic.Check() } -func init() { +var once sync.Once + +func delayedCrossMgrInit() { // hook interface checks into snapstate installation logic - snapstate.AddCheckSnapCallback(func(st *state.State, snapInfo, _ *snap.Info, _ snapstate.Flags) error { - return CheckInterfaces(st, snapInfo) + once.Do(func() { + snapstate.AddCheckSnapCallback(func(st *state.State, snapInfo, _ *snap.Info, _ snapstate.Flags) error { + return CheckInterfaces(st, snapInfo) + }) }) } diff -Nru snapd-2.28.5/overlord/ifacestate/ifacestate_test.go snapd-2.29.3/overlord/ifacestate/ifacestate_test.go --- snapd-2.28.5/overlord/ifacestate/ifacestate_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/ifacestate/ifacestate_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -32,6 +32,7 @@ "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/ifacetest" + "github.com/snapcore/snapd/overlord" "github.com/snapcore/snapd/overlord/assertstate" "github.com/snapcore/snapd/overlord/hookstate" "github.com/snapcore/snapd/overlord/ifacestate" @@ -45,28 +46,30 @@ func TestInterfaceManager(t *testing.T) { TestingT(t) } type interfaceManagerSuite struct { - state *state.State - db *asserts.Database - privateMgr *ifacestate.InterfaceManager - privateHookMgr *hookstate.HookManager - extraIfaces []interfaces.Interface - extraBackends []interfaces.SecurityBackend - secBackend *ifacetest.TestSecurityBackend - restoreBackends func() - mockSnapCmd *testutil.MockCmd - storeSigning *assertstest.StoreStack + testutil.BaseTest + o *overlord.Overlord + state *state.State + db *asserts.Database + privateMgr *ifacestate.InterfaceManager + privateHookMgr *hookstate.HookManager + extraIfaces []interfaces.Interface + extraBackends []interfaces.SecurityBackend + secBackend *ifacetest.TestSecurityBackend + mockSnapCmd *testutil.MockCmd + storeSigning *assertstest.StoreStack } var _ = Suite(&interfaceManagerSuite{}) func (s *interfaceManagerSuite) SetUpTest(c *C) { + s.BaseTest.SetUpTest(c) s.storeSigning = assertstest.NewStoreStack("canonical", nil) s.mockSnapCmd = testutil.MockCommand(c, "snap", "") dirs.SetRootDir(c.MkDir()) - state := state.New(nil) - s.state = state + s.o = overlord.Mock() + s.state = s.o.State() db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ Backstore: asserts.NewMemoryBackstore(), Trusted: s.storeSigning.Trusted, @@ -76,8 +79,10 @@ err = db.Add(s.storeSigning.StoreAccountKey("")) c.Assert(err, IsNil) + s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) + s.state.Lock() - assertstate.ReplaceDB(state, s.db) + assertstate.ReplaceDB(s.state, s.db) s.state.Unlock() s.privateHookMgr = nil @@ -88,17 +93,18 @@ // TODO: transition this so that we don't load real backends and instead // just load the test backend here and this is nicely integrated with // extraBackends above. - s.restoreBackends = ifacestate.MockSecurityBackends([]interfaces.SecurityBackend{s.secBackend}) + s.BaseTest.AddCleanup(ifacestate.MockSecurityBackends([]interfaces.SecurityBackend{s.secBackend})) } func (s *interfaceManagerSuite) TearDownTest(c *C) { + s.BaseTest.TearDownTest(c) + s.mockSnapCmd.Restore() if s.privateMgr != nil { s.privateMgr.Stop() } dirs.SetRootDir("") - s.restoreBackends() } func (s *interfaceManagerSuite) manager(c *C) *ifacestate.InterfaceManager { @@ -107,6 +113,7 @@ c.Assert(err, IsNil) mgr.AddForeignTaskHandlers() s.privateMgr = mgr + s.o.AddManager(mgr) } return s.privateMgr } @@ -116,17 +123,14 @@ mgr, err := hookstate.Manager(s.state) c.Assert(err, IsNil) s.privateHookMgr = mgr + s.o.AddManager(mgr) } return s.privateHookMgr } func (s *interfaceManagerSuite) settle(c *C) { - for i := 0; i < 50; i++ { - s.hookManager(c).Ensure() - s.manager(c).Ensure() - s.hookManager(c).Wait() - s.manager(c).Wait() - } + err := s.o.Settle(5 * time.Second) + c.Assert(err, IsNil) } func (s *interfaceManagerSuite) TestSmoke(c *C) { @@ -136,7 +140,7 @@ } func (s *interfaceManagerSuite) TestConnectTask(c *C) { - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) _ = s.manager(c) @@ -262,7 +266,7 @@ } func (s *interfaceManagerSuite) TestEnsureProcessesConnectTask(c *C) { - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) _ = s.manager(c) @@ -312,8 +316,7 @@ } func (s *interfaceManagerSuite) TestConnectTaskCheckInterfaceMismatch(c *C) { - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test2"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) _ = s.manager(c) @@ -346,7 +349,7 @@ } func (s *interfaceManagerSuite) TestConnectTaskNoSuchSlot(c *C) { - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) _ = s.manager(c) @@ -358,7 +361,7 @@ } func (s *interfaceManagerSuite) TestConnectTaskNoSuchPlug(c *C) { - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) _ = s.manager(c) @@ -435,7 +438,7 @@ - $SLOT_PUBLISHER_ID `)) defer restore() - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) setup() _ = s.manager(c) @@ -491,7 +494,7 @@ func (s *interfaceManagerSuite) testDisconnect(c *C, plugSnap, plugName, slotSnap, slotName string) { // Put two snaps in place They consumer has an plug that can be connected // to slot on the producer. - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) @@ -560,11 +563,15 @@ s.extraIfaces = append(s.extraIfaces, iface) } +func (s *interfaceManagerSuite) mockIfaces(c *C, ifaces ...interfaces.Interface) { + s.extraIfaces = append(s.extraIfaces, ifaces...) +} + func (s *interfaceManagerSuite) mockSnapDecl(c *C, name, publisher string, extraHeaders map[string]interface{}) { _, err := s.db.Find(asserts.AccountType, map[string]string{ "account-id": publisher, }) - if err == asserts.ErrNotFound { + if asserts.IsNotFound(err) { acct := assertstest.NewAccount(s.storeSigning, publisher, map[string]interface{}{ "account-id": publisher, }, "") @@ -604,7 +611,7 @@ decl := a[0].(*asserts.SnapDeclaration) snapInfo.SnapID = decl.SnapID() sideInfo.SnapID = decl.SnapID() - } else if err == asserts.ErrNotFound { + } else if asserts.IsNotFound(err) { err = nil } c.Assert(err, IsNil) @@ -843,7 +850,7 @@ // The setup-profiles task will auto-connect slots with viable candidates. func (s *interfaceManagerSuite) TestDoSetupSnapSecurityAutoConnectsSlots(c *C) { // Mock the interface that will be used by the test - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) // Add an OS snap. s.mockSnap(c, ubuntuCoreSnapYaml) // Add a consumer snap with unconnect plug (interface "test") @@ -892,7 +899,7 @@ // The setup-profiles task will auto-connect slots with viable multiple candidates. func (s *interfaceManagerSuite) TestDoSetupSnapSecurityAutoConnectsSlotsMultiplePlugs(c *C) { // Mock the interface that will be used by the test - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) // Add an OS snap. s.mockSnap(c, ubuntuCoreSnapYaml) // Add a consumer snap with unconnect plug (interface "test") @@ -1019,7 +1026,7 @@ `)) defer restore() // Add the producer snap - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) s.mockSnapDecl(c, "producer", "one-publisher", nil) s.mockSnap(c, producerYaml) @@ -1150,20 +1157,20 @@ } func (s *interfaceManagerSuite) TestDoSetupSnapSecuirtyReloadsConnectionsWhenInvokedOnPlugSide(c *C) { + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) snapInfo := s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) s.testDoSetupSnapSecuirtyReloadsConnectionsWhenInvokedOn(c, snapInfo.Name(), snapInfo.Revision) } func (s *interfaceManagerSuite) TestDoSetupSnapSecuirtyReloadsConnectionsWhenInvokedOnSlotSide(c *C) { + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) s.mockSnap(c, consumerYaml) snapInfo := s.mockSnap(c, producerYaml) s.testDoSetupSnapSecuirtyReloadsConnectionsWhenInvokedOn(c, snapInfo.Name(), snapInfo.Revision) } func (s *interfaceManagerSuite) testDoSetupSnapSecuirtyReloadsConnectionsWhenInvokedOn(c *C, snapName string, revision snap.Revision) { - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) - s.state.Lock() s.state.Set("conns", map[string]interface{}{ "consumer:plug producer:slot": map[string]interface{}{"interface": "test"}, @@ -1437,7 +1444,7 @@ } func (s *interfaceManagerSuite) TestDoRemove(c *C) { - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) @@ -1482,7 +1489,7 @@ } func (s *interfaceManagerSuite) TestConnectTracksConnectionsInState(c *C) { - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) @@ -1521,10 +1528,10 @@ } func (s *interfaceManagerSuite) TestConnectSetsUpSecurity(c *C) { - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) + s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) - _ = s.manager(c) s.state.Lock() @@ -1558,7 +1565,7 @@ } func (s *interfaceManagerSuite) TestDisconnectSetsUpSecurity(c *C) { - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) @@ -1603,7 +1610,7 @@ } func (s *interfaceManagerSuite) TestDisconnectTracksConnectionsInState(c *C) { - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) s.state.Lock() @@ -1643,7 +1650,7 @@ } func (s *interfaceManagerSuite) TestManagerReloadsConnections(c *C) { - s.mockIface(c, &ifacetest.TestInterface{InterfaceName: "test"}) + s.mockIfaces(c, &ifacetest.TestInterface{InterfaceName: "test"}, &ifacetest.TestInterface{InterfaceName: "test2"}) s.mockSnap(c, consumerYaml) s.mockSnap(c, producerYaml) @@ -1675,6 +1682,11 @@ InterfaceName: "test", }) c.Assert(err, IsNil) + err = repo.AddInterface(&ifacetest.TestInterface{ + InterfaceName: "test2", + }) + c.Assert(err, IsNil) + err = repo.AddSlot(&interfaces.Slot{ SlotInfo: &snap.SlotInfo{ Snap: siC, diff -Nru snapd-2.28.5/overlord/managers_test.go snapd-2.29.3/overlord/managers_test.go --- snapd-2.28.5/overlord/managers_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/managers_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -49,6 +49,7 @@ "github.com/snapcore/snapd/overlord/hookstate" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" "github.com/snapcore/snapd/partition" "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" @@ -61,29 +62,28 @@ type mgrsSuite struct { tempdir string - aa *testutil.MockCmd - udev *testutil.MockCmd - umount *testutil.MockCmd + restore func() - snapDiscardNs *testutil.MockCmd + aa *testutil.MockCmd + udev *testutil.MockCmd + umount *testutil.MockCmd + restoreSystemctl func() - prevctlCmd func(...string) ([]byte, error) + snapDiscardNs *testutil.MockCmd + snapSeccomp *testutil.MockCmd storeSigning *assertstest.StoreStack restoreTrusted func() - restore func() devAcct *asserts.Account - o *overlord.Overlord - serveIDtoName map[string]string serveSnapPath map[string]string serveRevision map[string]string hijackServeSnap func(http.ResponseWriter) - snapSeccomp *testutil.MockCmd + o *overlord.Overlord } var ( @@ -116,15 +116,14 @@ } os.Setenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS", "1") - snapstate.CanAutoRefresh = nil // create a fake systemd environment os.MkdirAll(filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants"), 0755) - ms.prevctlCmd = systemd.SystemctlCmd - systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) { + ms.restoreSystemctl = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { return []byte("ActiveState=inactive\n"), nil - } + }) + ms.aa = testutil.MockCommand(c, "apparmor_parser", "") ms.udev = testutil.MockCommand(c, "udevadm", "") ms.umount = testutil.MockCommand(c, "umount", "") @@ -140,12 +139,21 @@ err = ms.storeSigning.Add(ms.devAcct) c.Assert(err, IsNil) + ms.serveIDtoName = make(map[string]string) + ms.serveSnapPath = make(map[string]string) + ms.serveRevision = make(map[string]string) + + snapSeccompPath := filepath.Join(dirs.DistroLibExecDir, "snap-seccomp") + err = os.MkdirAll(filepath.Dir(snapSeccompPath), 0755) + c.Assert(err, IsNil) + ms.snapSeccomp = testutil.MockCommand(c, snapSeccompPath, "") + o, err := overlord.New() c.Assert(err, IsNil) ms.o = o st := ms.o.State() st.Lock() - // seeded + defer st.Unlock() st.Set("seeded", true) // registered auth.SetDevice(st, &auth.DeviceState{ @@ -153,24 +161,46 @@ Model: "generic-classic", Serial: "serialserial", }) - st.Unlock() - ms.serveIDtoName = make(map[string]string) - ms.serveSnapPath = make(map[string]string) - ms.serveRevision = make(map[string]string) - - snapSeccompPath := filepath.Join(dirs.DistroLibExecDir, "snap-seccomp") - err = os.MkdirAll(filepath.Dir(snapSeccompPath), 0755) + // add "core" snap declaration + headers := map[string]interface{}{ + "series": "16", + "snap-name": "core", + "publisher-id": "can0nical", + "timestamp": time.Now().Format(time.RFC3339), + } + headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) + err = assertstate.Add(st, ms.storeSigning.StoreAccountKey("")) c.Assert(err, IsNil) - ms.snapSeccomp = testutil.MockCommand(c, snapSeccompPath, "") + a, err := ms.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") + c.Assert(err, IsNil) + err = assertstate.Add(st, a) + c.Assert(err, IsNil) + ms.serveRevision["core"] = "1" + ms.serveIDtoName[fakeSnapID("core")] = "core" + err = ms.storeSigning.Add(a) + c.Assert(err, IsNil) + + // add core itself + snapstate.Set(st, "core", &snapstate.SnapState{ + Active: true, + Sequence: []*snap.SideInfo{ + {RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)}, + }, + Current: snap.R(1), + SnapType: "os", + }) + // don't actually try to talk to the store on snapstate.Ensure + // needs doing after the call to devicestate.Manager (which happens in overlord.New) + snapstate.CanAutoRefresh = nil } func (ms *mgrsSuite) TearDownTest(c *C) { dirs.SetRootDir("") ms.restoreTrusted() ms.restore() + ms.restoreSystemctl() os.Unsetenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS") - systemd.SystemctlCmd = ms.prevctlCmd ms.udev.Restore() ms.aa.Restore() ms.umount.Restore() @@ -178,6 +208,8 @@ ms.snapSeccomp.Restore() } +var settleTimeout = 15 * time.Second + func makeTestSnap(c *C, snapYamlContent string) string { return snaptest.MakeTestSnapWithFiles(c, snapYamlContent, nil) } @@ -200,7 +232,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -250,7 +282,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -349,7 +381,7 @@ } func (ms *mgrsSuite) mockStore(c *C) *httptest.Server { - var baseURL string + var baseURL *url.URL fillHit := func(name string) string { snapf, err := snap.Open(ms.serveSnapPath[name]) if err != nil { @@ -359,29 +391,32 @@ if err != nil { panic(err) } - hit := strings.Replace(searchHit, "@URL@", baseURL+"/snap/"+name, -1) + hit := strings.Replace(searchHit, "@URL@", baseURL.String()+"/api/v1/snaps/download/"+name, -1) hit = strings.Replace(hit, "@NAME@", name, -1) hit = strings.Replace(hit, "@SNAPID@", fakeSnapID(name), -1) - hit = strings.Replace(hit, "@ICON@", baseURL+"/icon", -1) + hit = strings.Replace(hit, "@ICON@", baseURL.String()+"/icon", -1) hit = strings.Replace(hit, "@VERSION@", info.Version, -1) hit = strings.Replace(hit, "@REVISION@", ms.serveRevision[name], -1) return hit } mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // all URLS are /api/v1/snaps/... so check the url is sane and discard + // the common prefix to simplify indexing into the comps slice. comps := strings.Split(r.URL.Path, "/") - if len(comps) == 0 { + if len(comps) <= 4 { panic("unexpected url path: " + r.URL.Path) - } - switch comps[1] { + comps = comps[4:] + + switch comps[0] { case "assertions": ref := &asserts.Ref{ - Type: asserts.Type(comps[2]), - PrimaryKey: comps[3:], + Type: asserts.Type(comps[1]), + PrimaryKey: comps[2:], } a, err := ref.Resolve(ms.storeSigning.Find) - if err == asserts.ErrNotFound { + if asserts.IsNotFound(err) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(404) w.Write([]byte(`{"status": 404}`)) @@ -396,7 +431,7 @@ return case "details": w.WriteHeader(200) - io.WriteString(w, fillHit(comps[2])) + io.WriteString(w, fillHit(comps[1])) case "metadata": dec := json.NewDecoder(r.Body) var input struct { @@ -427,12 +462,12 @@ panic(err) } w.Write(output) - case "snap": + case "download": if ms.hijackServeSnap != nil { ms.hijackServeSnap(w) return } - snapR, err := os.Open(ms.serveSnapPath[comps[2]]) + snapR, err := os.Open(ms.serveSnapPath[comps[1]]) if err != nil { panic(err) } @@ -443,24 +478,17 @@ })) c.Assert(mockServer, NotNil) - baseURL = mockServer.URL - - detailsURL, err := url.Parse(baseURL + "/details/") - c.Assert(err, IsNil) - bulkURL, err := url.Parse(baseURL + "/metadata") - c.Assert(err, IsNil) - assertionsURL, err := url.Parse(baseURL + "/assertions/") - c.Assert(err, IsNil) + baseURL, _ = url.Parse(mockServer.URL) + assertionsBaseURL, _ := baseURL.Parse("api/v1/snaps") storeCfg := store.Config{ - DetailsURI: detailsURL, - BulkURI: bulkURL, - AssertionsURI: assertionsURL, + StoreBaseURL: baseURL, + AssertionsBaseURL: assertionsBaseURL, } mStore := store.New(&storeCfg, nil) st := ms.o.State() st.Lock() - snapstate.ReplaceStore(ms.o.State(), mStore) + storestate.ReplaceStore(ms.o.State(), mStore) st.Unlock() return mockServer @@ -516,7 +544,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -565,7 +593,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -618,9 +646,7 @@ defer st.Unlock() // have the snap-declaration in the system db - err := assertstate.Add(st, ms.storeSigning.StoreAccountKey("")) - c.Assert(err, IsNil) - err = assertstate.Add(st, ms.devAcct) + err := assertstate.Add(st, ms.devAcct) c.Assert(err, IsNil) err = assertstate.Add(st, snapDecl) c.Assert(err, IsNil) @@ -631,7 +657,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -688,9 +714,7 @@ defer st.Unlock() // have the snap-declaration in the system db - err := assertstate.Add(st, ms.storeSigning.StoreAccountKey("")) - c.Assert(err, IsNil) - err = assertstate.Add(st, ms.devAcct) + err := assertstate.Add(st, ms.devAcct) c.Assert(err, IsNil) err = assertstate.Add(st, snapDecl) c.Assert(err, IsNil) @@ -701,7 +725,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -738,7 +762,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -816,7 +840,7 @@ chg.AddAll(tss[0]) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -856,7 +880,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -876,7 +900,7 @@ bootloader.BootVars["snap_core"] = "core_x1.snap" st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -929,8 +953,6 @@ defer st.Unlock() // setup model assertion - err = assertstate.Add(st, ms.storeSigning.StoreAccountKey("")) - c.Assert(err, IsNil) err = assertstate.Add(st, brandAcct) c.Assert(err, IsNil) err = assertstate.Add(st, brandAccKey) @@ -948,7 +970,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -980,7 +1002,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -999,7 +1021,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -1044,7 +1066,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -1083,7 +1105,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -1129,7 +1151,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -1149,7 +1171,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -1202,7 +1224,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -1262,7 +1284,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -1307,7 +1329,7 @@ chg.AddAll(tss[0]) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -1367,7 +1389,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -1408,7 +1430,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -1480,7 +1502,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -1494,7 +1516,7 @@ chg.AddAll(ts) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -1556,7 +1578,7 @@ chg.AddAll(tss[2]) st.Unlock() - err = ms.o.Settle() + err = ms.o.Settle(settleTimeout) st.Lock() c.Assert(err, IsNil) @@ -1695,11 +1717,11 @@ err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755) c.Assert(err, IsNil) - captureAuthContext := func(_ *store.Config, ac auth.AuthContext) *store.Store { + captureAuthContext := func(_ *state.State, ac auth.AuthContext) error { s.ac = ac return nil } - r := overlord.MockStoreNew(captureAuthContext) + r := overlord.MockSetupStore(captureAuthContext) defer r() s.storeSigning = assertstest.NewStoreStack("can0nical", nil) diff -Nru snapd-2.28.5/overlord/overlord.go snapd-2.29.3/overlord/overlord.go --- snapd-2.28.5/overlord/overlord.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/overlord/overlord.go 2017-10-23 06:17:27.000000000 +0000 @@ -1,7 +1,7 @@ // -*- Mode: Go; indent-tabs-mode: t -*- /* - * Copyright (C) 2016 Canonical Ltd + * Copyright (C) 2016-2017 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 @@ -43,7 +43,7 @@ "github.com/snapcore/snapd/overlord/patch" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" - "github.com/snapcore/snapd/store" + "github.com/snapcore/snapd/overlord/storestate" ) var ( @@ -68,6 +68,7 @@ // restarts restartHandler func(t state.RestartType) // managers + inited bool snapMgr *snapstate.SnapManager assertMgr *assertstate.AssertManager ifaceMgr *ifacestate.InterfaceManager @@ -77,12 +78,13 @@ cmdMgr *cmdstate.CommandManager } -var storeNew = store.New +var setupStore = storestate.SetupStore // New creates a new Overlord with all its state managers. func New() (*Overlord, error) { o := &Overlord{ loopTomb: new(tomb.Tomb), + inited: true, } backend := &overlordStateBackend{ @@ -101,30 +103,27 @@ if err != nil { return nil, err } - o.hookMgr = hookMgr - o.stateEng.AddManager(o.hookMgr) + o.addManager(hookMgr) snapMgr, err := snapstate.Manager(s) if err != nil { return nil, err } - o.snapMgr = snapMgr - o.stateEng.AddManager(o.snapMgr) + o.addManager(snapMgr) assertMgr, err := assertstate.Manager(s) if err != nil { return nil, err } - o.assertMgr = assertMgr - o.stateEng.AddManager(o.assertMgr) + o.addManager(assertMgr) ifaceMgr, err := ifacestate.Manager(s, hookMgr, nil, nil) if err != nil { return nil, err } - o.ifaceMgr = ifaceMgr - o.stateEng.AddManager(o.ifaceMgr) + o.addManager(ifaceMgr) + // TODO: this is a bit weird, not actually a StateManager configMgr, err := configstate.Manager(s, hookMgr) if err != nil { return nil, err @@ -135,25 +134,45 @@ if err != nil { return nil, err } - o.deviceMgr = deviceMgr - o.stateEng.AddManager(o.deviceMgr) + o.addManager(deviceMgr) - o.cmdMgr = cmdstate.Manager(s) - o.stateEng.AddManager(o.cmdMgr) + o.addManager(cmdstate.Manager(s)) + + s.Lock() + defer s.Unlock() // setting up the store authContext := auth.NewAuthContext(s, o.deviceMgr) - sto := storeNew(nil, authContext) - s.Lock() - snapstate.ReplaceStore(s, sto) + err = setupStore(s, authContext) + if err != nil { + return nil, err + } + if err := o.snapMgr.GenerateCookies(s); err != nil { return nil, fmt.Errorf("failed to generate cookies: %q", err) } - s.Unlock() return o, nil } +func (o *Overlord) addManager(mgr StateManager) { + switch x := mgr.(type) { + case *hookstate.HookManager: + o.hookMgr = x + case *snapstate.SnapManager: + o.snapMgr = x + case *assertstate.AssertManager: + o.assertMgr = x + case *ifacestate.InterfaceManager: + o.ifaceMgr = x + case *devicestate.DeviceManager: + o.deviceMgr = x + case *cmdstate.CommandManager: + o.cmdMgr = x + } + o.stateEng.AddManager(mgr) +} + func loadState(backend state.Backend) (*state.State, error) { if !osutil.FileExists(dirs.SnapStateFile) { // fail fast, mostly interesting for tests, this dir is setup @@ -263,9 +282,10 @@ // Settle runs first a state engine Ensure and then wait for activities to settle. // That's done by waiting for all managers activities to settle while -// making sure no immediate further Ensure is scheduled. Chiefly for tests. -// Cannot be used in conjunction with Loop. -func (o *Overlord) Settle() error { +// making sure no immediate further Ensure is scheduled. Chiefly for +// tests. Cannot be used in conjunction with Loop. If timeout is +// non-zero and settling takes longer than timeout, returns an error. +func (o *Overlord) Settle(timeout time.Duration) error { func() { o.ensureLock.Lock() defer o.ensureLock.Unlock() @@ -282,9 +302,17 @@ o.ensureTimer = nil }() + t0 := time.Now() done := false var errs []error for !done { + if timeout > 0 && time.Since(t0) > timeout { + err := fmt.Errorf("Settle is not converging") + if len(errs) != 0 { + return &ensureError{append(errs, err)} + } + return err + } next := o.ensureTimerReset() err := o.stateEng.Ensure() switch ee := err.(type) { @@ -298,6 +326,18 @@ o.ensureLock.Lock() done = o.ensureNext.Equal(next) o.ensureLock.Unlock() + if done { + // we should wait also for cleanup handlers + st := o.State() + st.Lock() + for _, chg := range st.Changes() { + if chg.IsReady() && !chg.IsClean() { + done = false + break + } + } + st.Unlock() + } } if len(errs) != 0 { return &ensureError{errs} @@ -343,3 +383,46 @@ func (o *Overlord) CommandManager() *cmdstate.CommandManager { return o.cmdMgr } + +// Mock creates an Overlord without any managers and with a backend +// not using disk. Managers can be added with AddManager. For testing. +func Mock() *Overlord { + o := &Overlord{ + loopTomb: new(tomb.Tomb), + inited: false, + } + o.stateEng = NewStateEngine(state.New(mockBackend{o: o})) + return o +} + +// AddManager adds a manager to the overlord created with Mock. For +// testing. +func (o *Overlord) AddManager(mgr StateManager) { + if o.inited { + panic("internal error: cannot add managers to a fully initialized Overlord") + } + o.addManager(mgr) +} + +type mockBackend struct { + o *Overlord +} + +func (mb mockBackend) Checkpoint(data []byte) error { + return nil +} + +func (mb mockBackend) EnsureBefore(d time.Duration) { + mb.o.ensureLock.Lock() + timer := mb.o.ensureTimer + mb.o.ensureLock.Unlock() + if timer == nil { + return + } + + mb.o.ensureBefore(d) +} + +func (mb mockBackend) RequestRestart(t state.RestartType) { + mb.o.requestRestart(t) +} diff -Nru snapd-2.28.5/overlord/overlord_test.go snapd-2.29.3/overlord/overlord_test.go --- snapd-2.28.5/overlord/overlord_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/overlord_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -1,7 +1,7 @@ // -*- Mode: Go; indent-tabs-mode: t -*- /* - * Copyright (C) 2016 Canonical Ltd + * Copyright (C) 2016-2017 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 @@ -21,6 +21,7 @@ import ( "encoding/json" + "errors" "fmt" "io/ioutil" "os" @@ -38,7 +39,6 @@ "github.com/snapcore/snapd/overlord/patch" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" - "github.com/snapcore/snapd/store" "github.com/snapcore/snapd/testutil" ) @@ -63,6 +63,12 @@ restore := patch.Mock(42, nil) defer restore() + var setupStoreAuthContext auth.AuthContext + defer overlord.MockSetupStore(func(_ *state.State, ac auth.AuthContext) error { + setupStoreAuthContext = ac + return nil + })() + o, err := overlord.New() c.Assert(err, IsNil) c.Check(o, NotNil) @@ -70,7 +76,9 @@ c.Check(o.SnapManager(), NotNil) c.Check(o.AssertManager(), NotNil) c.Check(o.InterfaceManager(), NotNil) + c.Check(o.HookManager(), NotNil) c.Check(o.DeviceManager(), NotNil) + c.Check(o.CommandManager(), NotNil) s := o.State() c.Check(s, NotNil) @@ -82,9 +90,8 @@ s.Get("patch-level", &patchLevel) c.Check(patchLevel, Equals, 42) - // store is setup - sto := snapstate.Store(s) - c.Check(sto, FitsTypeOf, &store.Store{}) + // store was setup with an auth context + c.Check(setupStoreAuthContext, NotNil) } func (ovs *overlordSuite) TestNewWithGoodState(c *C) { @@ -151,6 +158,15 @@ c.Check(b, Equals, true) } +func (ovs *overlordSuite) TestNewWithSetupStoreError(c *C) { + defer overlord.MockSetupStore(func(*state.State, auth.AuthContext) error { + return errors.New("fake error") + })() + + _, err := overlord.New() + c.Check(err, ErrorMatches, "fake error") +} + type witnessManager struct { state *state.State expectedEnsure int @@ -202,17 +218,15 @@ func (ovs *overlordSuite) TestEnsureLoopRunAndStop(c *C) { restoreIntv := overlord.MockEnsureInterval(10 * time.Millisecond) defer restoreIntv() - o, err := overlord.New() - c.Assert(err, IsNil) + o := overlord.Mock() witness := &witnessManager{ state: o.State(), expectedEnsure: 3, ensureCalled: make(chan struct{}), } - o.Engine().AddManager(witness) + o.AddManager(witness) - markSeeded(o) o.Loop() defer o.Stop() @@ -224,15 +238,14 @@ } c.Check(time.Since(t0) >= 10*time.Millisecond, Equals, true) - err = o.Stop() + err := o.Stop() c.Assert(err, IsNil) } func (ovs *overlordSuite) TestEnsureLoopMediatedEnsureBeforeImmediate(c *C) { restoreIntv := overlord.MockEnsureInterval(10 * time.Minute) defer restoreIntv() - o, err := overlord.New() - c.Assert(err, IsNil) + o := overlord.Mock() ensure := func(s *state.State) error { s.EnsureBefore(0) @@ -245,10 +258,8 @@ ensureCalled: make(chan struct{}), ensureCallback: ensure, } - se := o.Engine() - se.AddManager(witness) + o.AddManager(witness) - markSeeded(o) o.Loop() defer o.Stop() @@ -262,8 +273,7 @@ func (ovs *overlordSuite) TestEnsureLoopMediatedEnsureBefore(c *C) { restoreIntv := overlord.MockEnsureInterval(10 * time.Minute) defer restoreIntv() - o, err := overlord.New() - c.Assert(err, IsNil) + o := overlord.Mock() ensure := func(s *state.State) error { s.EnsureBefore(10 * time.Millisecond) @@ -276,10 +286,8 @@ ensureCalled: make(chan struct{}), ensureCallback: ensure, } - se := o.Engine() - se.AddManager(witness) + o.AddManager(witness) - markSeeded(o) o.Loop() defer o.Stop() @@ -293,9 +301,7 @@ func (ovs *overlordSuite) TestEnsureBeforeSleepy(c *C) { restoreIntv := overlord.MockEnsureInterval(10 * time.Minute) defer restoreIntv() - - o, err := overlord.New() - c.Assert(err, IsNil) + o := overlord.Mock() ensure := func(s *state.State) error { overlord.MockEnsureNext(o, time.Now().Add(-10*time.Hour)) @@ -309,10 +315,8 @@ ensureCalled: make(chan struct{}), ensureCallback: ensure, } - se := o.Engine() - se.AddManager(witness) + o.AddManager(witness) - markSeeded(o) o.Loop() defer o.Stop() @@ -326,8 +330,7 @@ func (ovs *overlordSuite) TestEnsureLoopMediatedEnsureBeforeOutsideEnsure(c *C) { restoreIntv := overlord.MockEnsureInterval(10 * time.Minute) defer restoreIntv() - o, err := overlord.New() - c.Assert(err, IsNil) + o := overlord.Mock() ch := make(chan struct{}) ensure := func(s *state.State) error { @@ -341,10 +344,8 @@ ensureCalled: make(chan struct{}), ensureCallback: ensure, } - se := o.Engine() - se.AddManager(witness) + o.AddManager(witness) - markSeeded(o) o.Loop() defer o.Stop() @@ -354,7 +355,7 @@ c.Fatal("Ensure calls not happening") } - se.State().EnsureBefore(0) + o.State().EnsureBefore(0) select { case <-witness.ensureCalled: @@ -366,8 +367,7 @@ func (ovs *overlordSuite) TestEnsureLoopPrune(c *C) { restoreIntv := overlord.MockPruneInterval(200*time.Millisecond, 1000*time.Millisecond, 1000*time.Millisecond) defer restoreIntv() - o, err := overlord.New() - c.Assert(err, IsNil) + o := overlord.Mock() st := o.State() st.Lock() @@ -400,10 +400,8 @@ witness := &witnessManager{ ensureCallback: waitForPrune, } - se := o.Engine() - se.AddManager(witness) + o.AddManager(witness) - markSeeded(o) o.Loop() select { @@ -412,7 +410,7 @@ c.Fatal("Pruning should have happened by now") } - err = o.Stop() + err := o.Stop() c.Assert(err, IsNil) st.Lock() @@ -427,8 +425,7 @@ func (ovs *overlordSuite) TestEnsureLoopPruneRunsMultipleTimes(c *C) { restoreIntv := overlord.MockPruneInterval(100*time.Millisecond, 1000*time.Millisecond, 1*time.Hour) defer restoreIntv() - o, err := overlord.New() - c.Assert(err, IsNil) + o := overlord.Mock() // create two changes, one that can be pruned now, one in progress st := o.State() @@ -444,7 +441,6 @@ c.Check(st.Changes(), HasLen, 2) st.Unlock() - markSeeded(o) // start the loop that runs the prune ticker o.Loop() @@ -464,7 +460,7 @@ st.Unlock() // cleanup loop ticker - err = o.Stop() + err := o.Stop() c.Assert(err, IsNil) } @@ -520,6 +516,27 @@ s.EnsureBefore(20 * time.Millisecond) return nil }, nil) + rm.runner.AddHandler("runMgrForever", func(t *state.Task, _ *tomb.Tomb) error { + s := t.State() + s.Lock() + defer s.Unlock() + s.EnsureBefore(20 * time.Millisecond) + return &state.Retry{} + }, nil) + rm.runner.AddHandler("runMgrWCleanup", func(t *state.Task, _ *tomb.Tomb) error { + s := t.State() + s.Lock() + defer s.Unlock() + s.Set("runMgrWCleanupMark", 1) + return nil + }, nil) + rm.runner.AddCleanup("runMgrWCleanup", func(t *state.Task, _ *tomb.Tomb) error { + s := t.State() + s.Lock() + defer s.Unlock() + s.Set("runMgrWCleanupCleanedUp", 1) + return nil + }) return rm } @@ -543,13 +560,11 @@ func (ovs *overlordSuite) TestTrivialSettle(c *C) { restoreIntv := overlord.MockEnsureInterval(1 * time.Minute) defer restoreIntv() - o, err := overlord.New() - c.Assert(err, IsNil) + o := overlord.Mock() - se := o.Engine() - s := se.State() + s := o.State() rm1 := newRunnerManager(s) - se.AddManager(rm1) + o.AddManager(rm1) defer o.Engine().Stop() @@ -561,28 +576,49 @@ chg.AddTask(t1) s.Unlock() - - markSeeded(o) - o.Settle() - + o.Settle(5 * time.Second) s.Lock() c.Check(t1.Status(), Equals, state.DoneStatus) var v int - err = s.Get("runMgr1Mark", &v) + err := s.Get("runMgr1Mark", &v) c.Check(err, IsNil) } +func (ovs *overlordSuite) TestSettleNotConverging(c *C) { + restoreIntv := overlord.MockEnsureInterval(1 * time.Minute) + defer restoreIntv() + o := overlord.Mock() + + s := o.State() + rm1 := newRunnerManager(s) + o.AddManager(rm1) + + defer o.Engine().Stop() + + s.Lock() + defer s.Unlock() + + chg := s.NewChange("chg", "...") + t1 := s.NewTask("runMgrForever", "1...") + chg.AddTask(t1) + + s.Unlock() + err := o.Settle(250 * time.Millisecond) + s.Lock() + + c.Check(err, ErrorMatches, `Settle is not converging`) + +} + func (ovs *overlordSuite) TestSettleChain(c *C) { restoreIntv := overlord.MockEnsureInterval(1 * time.Minute) defer restoreIntv() - o, err := overlord.New() - c.Assert(err, IsNil) + o := overlord.Mock() - se := o.Engine() - s := se.State() + s := o.State() rm1 := newRunnerManager(s) - se.AddManager(rm1) + o.AddManager(rm1) defer o.Engine().Stop() @@ -596,29 +632,60 @@ chg.AddAll(state.NewTaskSet(t1, t2)) s.Unlock() + o.Settle(5 * time.Second) + s.Lock() + c.Check(t1.Status(), Equals, state.DoneStatus) + c.Check(t2.Status(), Equals, state.DoneStatus) - markSeeded(o) - o.Settle() + var v int + err := s.Get("runMgr1Mark", &v) + c.Check(err, IsNil) + err = s.Get("runMgr2Mark", &v) + c.Check(err, IsNil) +} + +func (ovs *overlordSuite) TestSettleChainWCleanup(c *C) { + restoreIntv := overlord.MockEnsureInterval(1 * time.Minute) + defer restoreIntv() + o := overlord.Mock() + + s := o.State() + rm1 := newRunnerManager(s) + o.AddManager(rm1) + + defer o.Engine().Stop() s.Lock() + defer s.Unlock() + + chg := s.NewChange("chg", "...") + t1 := s.NewTask("runMgrWCleanup", "1...") + t2 := s.NewTask("runMgr2", "2...") + t2.WaitFor(t1) + chg.AddAll(state.NewTaskSet(t1, t2)) + + s.Unlock() + o.Settle(5 * time.Second) + s.Lock() c.Check(t1.Status(), Equals, state.DoneStatus) c.Check(t2.Status(), Equals, state.DoneStatus) var v int - err = s.Get("runMgr1Mark", &v) + err := s.Get("runMgrWCleanupMark", &v) c.Check(err, IsNil) err = s.Get("runMgr2Mark", &v) c.Check(err, IsNil) + + err = s.Get("runMgrWCleanupCleanedUp", &v) + c.Check(err, IsNil) } func (ovs *overlordSuite) TestSettleExplicitEnsureBefore(c *C) { restoreIntv := overlord.MockEnsureInterval(1 * time.Minute) defer restoreIntv() - o, err := overlord.New() - c.Assert(err, IsNil) + o := overlord.Mock() - se := o.Engine() - s := se.State() + s := o.State() rm1 := newRunnerManager(s) rm1.ensureCallback = func() { s.Lock() @@ -628,7 +695,7 @@ s.Set("ensureCount", v+1) } - se.AddManager(rm1) + o.AddManager(rm1) defer o.Engine().Stop() @@ -638,16 +705,14 @@ chg := s.NewChange("chg", "...") t := s.NewTask("runMgrEnsureBefore", "...") chg.AddTask(t) - s.Unlock() - - markSeeded(o) - o.Settle() + s.Unlock() + o.Settle(5 * time.Second) s.Lock() c.Check(t.Status(), Equals, state.DoneStatus) var v int - err = s.Get("ensureCount", &v) + err := s.Get("ensureCount", &v) c.Check(err, IsNil) c.Check(v, Equals, 2) } diff -Nru snapd-2.28.5/overlord/patch/export_test.go snapd-2.29.3/overlord/patch/export_test.go --- snapd-2.28.5/overlord/patch/export_test.go 2016-11-24 09:36:04.000000000 +0000 +++ snapd-2.29.3/overlord/patch/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -1,3 +1,22 @@ +// -*- 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 . + * + */ + package patch import ( diff -Nru snapd-2.28.5/overlord/patch/patch3.go snapd-2.29.3/overlord/patch/patch3.go --- snapd-2.28.5/overlord/patch/patch3.go 2016-12-08 15:14:07.000000000 +0000 +++ snapd-2.29.3/overlord/patch/patch3.go 2017-10-23 06:17:27.000000000 +0000 @@ -22,7 +22,7 @@ import ( "fmt" - "github.com/snapcore/snapd/i18n/dumb" + "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/overlord/state" ) diff -Nru snapd-2.28.5/overlord/servicestate/servicestate.go snapd-2.29.3/overlord/servicestate/servicestate.go --- snapd-2.28.5/overlord/servicestate/servicestate.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/overlord/servicestate/servicestate.go 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,91 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2015-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 . + * + */ + +package servicestate + +import ( + "fmt" + + "github.com/snapcore/snapd/client" + "github.com/snapcore/snapd/overlord/cmdstate" + "github.com/snapcore/snapd/overlord/snapstate" + "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/snap" +) + +type Instruction struct { + Action string `json:"action"` + Names []string `json:"names"` + client.StartOptions + client.StopOptions + client.RestartOptions +} + +type ServiceActionConflictError struct{ error } + +func Control(st *state.State, appInfos []*snap.AppInfo, inst *Instruction) (*state.TaskSet, error) { + // the argv to call systemctl will need at most one entry per appInfo, + // plus one for "systemctl", one for the action, and sometimes one for + // an option. That's a maximum of 3+len(appInfos). + argv := make([]string, 2, 3+len(appInfos)) + argv[0] = "systemctl" + + argv[1] = inst.Action + switch inst.Action { + case "start": + if inst.Enable { + argv[1] = "enable" + argv = append(argv, "--now") + } + case "stop": + if inst.Disable { + argv[1] = "disable" + argv = append(argv, "--now") + } + case "restart": + if inst.Reload { + argv[1] = "reload-or-restart" + } + default: + return nil, fmt.Errorf("unknown action %q", inst.Action) + } + + snapNames := make([]string, 0, len(appInfos)) + lastName := "" + names := make([]string, len(appInfos)) + for i, svc := range appInfos { + argv = append(argv, svc.ServiceName()) + snapName := svc.Snap.Name() + names[i] = snapName + "." + svc.Name + if snapName != lastName { + snapNames = append(snapNames, snapName) + lastName = snapName + } + } + + desc := fmt.Sprintf("%s of %v", inst.Action, names) + + st.Lock() + defer st.Unlock() + if err := snapstate.CheckChangeConflictMany(st, snapNames, nil); err != nil { + return nil, &ServiceActionConflictError{err} + } + + return cmdstate.Exec(st, desc, argv), nil +} diff -Nru snapd-2.28.5/overlord/snapstate/aliasesv2.go snapd-2.29.3/overlord/snapstate/aliasesv2.go --- snapd-2.28.5/overlord/snapstate/aliasesv2.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/aliasesv2.go 2017-10-23 06:17:27.000000000 +0000 @@ -24,7 +24,7 @@ "fmt" "strings" - "github.com/snapcore/snapd/i18n/dumb" + "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/overlord/snapstate/backend" "github.com/snapcore/snapd/overlord/state" diff -Nru snapd-2.28.5/overlord/snapstate/aliasesv2_test.go snapd-2.29.3/overlord/snapstate/aliasesv2_test.go --- snapd-2.28.5/overlord/snapstate/aliasesv2_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/aliasesv2_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -229,6 +229,7 @@ c.Check(dropped, HasLen, 0) c.Check(seen, DeepEquals, map[string]bool{ + "core": true, "alias-snap": true, "other-snap": true, }) @@ -552,7 +553,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("%v", chg.Err())) @@ -667,7 +668,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("%v", chg.Err())) @@ -815,7 +816,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("%v", chg.Err())) @@ -903,7 +904,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("%v", chg.Err())) @@ -955,7 +956,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("%v", chg.Err())) @@ -1123,7 +1124,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("%v", chg.Err())) diff -Nru snapd-2.28.5/overlord/snapstate/backend/copydata_test.go snapd-2.29.3/overlord/snapstate/backend/copydata_test.go --- snapd-2.28.5/overlord/snapstate/backend/copydata_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/backend/copydata_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -40,9 +40,8 @@ ) type copydataSuite struct { - be backend.Backend - nullProgress progress.NullProgress - tempdir string + be backend.Backend + tempdir string } var _ = Suite(©dataSuite{}) @@ -79,7 +78,7 @@ v1 := snaptest.MockSnap(c, helloYaml1, helloContents, &snap.SideInfo{Revision: snap.R(10)}) // just creates data dirs in this case - err = s.be.CopySnapData(v1, nil, &s.nullProgress) + err = s.be.CopySnapData(v1, nil, progress.Null) c.Assert(err, IsNil) canaryDataFile := filepath.Join(v1.DataDir(), "canary.txt") @@ -94,7 +93,7 @@ c.Assert(err, IsNil) v2 := snaptest.MockSnap(c, helloYaml2, helloContents, &snap.SideInfo{Revision: snap.R(20)}) - err = s.be.CopySnapData(v2, v1, &s.nullProgress) + err = s.be.CopySnapData(v2, v1, progress.Null) c.Assert(err, IsNil) newCanaryDataFile := filepath.Join(dirs.SnapDataDir, "hello/20", "canary.txt") @@ -125,11 +124,11 @@ defer func() { dirs.SnapDataHomeGlob = oldSnapDataHomeGlob }() v1 := snaptest.MockSnap(c, helloYaml1, helloContents, &snap.SideInfo{Revision: snap.R(10)}) - c.Assert(s.be.CopySnapData(v1, nil, &s.nullProgress), IsNil) + c.Assert(s.be.CopySnapData(v1, nil, progress.Null), IsNil) c.Assert(os.Chmod(v1.DataDir(), 0), IsNil) v2 := snaptest.MockSnap(c, helloYaml2, helloContents, &snap.SideInfo{Revision: snap.R(20)}) - err := s.be.CopySnapData(v2, v1, &s.nullProgress) + err := s.be.CopySnapData(v2, v1, progress.Null) c.Check(err, ErrorMatches, "cannot copy .*") } @@ -142,7 +141,7 @@ dirs.SnapDataHomeGlob = filepath.Join(s.tempdir, "no-such-home", "*", "snap") v1 := snaptest.MockSnap(c, helloYaml1, helloContents, &snap.SideInfo{Revision: snap.R(10)}) - err := s.be.CopySnapData(v1, nil, &s.nullProgress) + err := s.be.CopySnapData(v1, nil, progress.Null) c.Assert(err, IsNil) canaryDataFile := filepath.Join(v1.DataDir(), "canary.txt") @@ -153,7 +152,7 @@ c.Assert(err, IsNil) v2 := snaptest.MockSnap(c, helloYaml2, helloContents, &snap.SideInfo{Revision: snap.R(20)}) - err = s.be.CopySnapData(v2, v1, &s.nullProgress) + err = s.be.CopySnapData(v2, v1, progress.Null) c.Assert(err, IsNil) _, err = os.Stat(filepath.Join(v2.DataDir(), "canary.txt")) @@ -193,7 +192,8 @@ c.Assert(err, IsNil) err = ioutil.WriteFile(filepath.Join(homeData, "canary.home"), []byte(fmt.Sprintln(revision)), 0644) c.Assert(err, IsNil) - return + + return homedir } func (s *copydataSuite) TestCopyDataDoUndo(c *C) { @@ -205,7 +205,7 @@ v2 := snaptest.MockSnap(c, helloYaml2, helloContents, &snap.SideInfo{Revision: snap.R(20)}) // copy data - err := s.be.CopySnapData(v2, v1, &s.nullProgress) + err := s.be.CopySnapData(v2, v1, progress.Null) c.Assert(err, IsNil) v2data := filepath.Join(dirs.SnapDataDir, "hello/20") l, err := filepath.Glob(filepath.Join(v2data, "*")) @@ -216,7 +216,7 @@ c.Assert(err, IsNil) c.Assert(l, HasLen, 1) - err = s.be.UndoCopySnapData(v2, v1, &s.nullProgress) + err = s.be.UndoCopySnapData(v2, v1, progress.Null) c.Assert(err, IsNil) // now removed @@ -239,14 +239,14 @@ v2 := snaptest.MockSnap(c, helloYaml2, helloContents, &snap.SideInfo{Revision: snap.R(20)}) // copy data - err := s.be.CopySnapData(v2, v1, &s.nullProgress) + err := s.be.CopySnapData(v2, v1, progress.Null) c.Assert(err, IsNil) v2data := filepath.Join(dirs.SnapDataDir, "hello/20") l, err := filepath.Glob(filepath.Join(v2data, "*")) c.Assert(err, IsNil) c.Assert(l, HasLen, 1) - err = s.be.UndoCopySnapData(v2, v1, &s.nullProgress) + err = s.be.UndoCopySnapData(v2, v1, progress.Null) c.Assert(err, IsNil) // now removed @@ -258,14 +258,14 @@ v1 := snaptest.MockSnap(c, helloYaml1, helloContents, &snap.SideInfo{Revision: snap.R(10)}) // first install - err := s.be.CopySnapData(v1, nil, &s.nullProgress) + err := s.be.CopySnapData(v1, nil, progress.Null) c.Assert(err, IsNil) _, err = os.Stat(v1.DataDir()) c.Assert(err, IsNil) _, err = os.Stat(v1.CommonDataDir()) c.Assert(err, IsNil) - err = s.be.UndoCopySnapData(v1, nil, &s.nullProgress) + err = s.be.UndoCopySnapData(v1, nil, progress.Null) c.Assert(err, IsNil) _, err = os.Stat(v1.DataDir()) c.Check(os.IsNotExist(err), Equals, true) @@ -285,7 +285,7 @@ c.Check(s.populatedData("20"), Equals, "20\n") // and now we pretend to refresh back to v1 (r10) - c.Check(s.be.CopySnapData(v1, v2, &s.nullProgress), IsNil) + c.Check(s.be.CopySnapData(v1, v2, progress.Null), IsNil) // so 10 now has 20's data c.Check(s.populatedData("10"), Equals, "20\n") @@ -310,14 +310,14 @@ c.Check(s.populatedData("20"), Equals, "20\n") // and now we pretend to refresh back to v1 (r10) - c.Check(s.be.CopySnapData(v1, v2, &s.nullProgress), IsNil) + c.Check(s.be.CopySnapData(v1, v2, progress.Null), IsNil) // so v1 (r10) now has v2 (r20)'s data and we have trash c.Check(s.populatedData("10"), Equals, "20\n") c.Check(s.populatedData("10.old"), Equals, "10\n") // but oh no! we have to undo it! - c.Check(s.be.UndoCopySnapData(v1, v2, &s.nullProgress), IsNil) + c.Check(s.be.UndoCopySnapData(v1, v2, progress.Null), IsNil) // so now v1 (r10) has v1 (r10)'s data and v2 (r20) has v2 (r20)'s data and we have no trash c.Check(s.populatedData("10"), Equals, "10\n") @@ -337,10 +337,10 @@ v2 := snaptest.MockSnap(c, helloYaml2, helloContents, &snap.SideInfo{Revision: snap.R(20)}) // copy data - err := s.be.CopySnapData(v2, v1, &s.nullProgress) + err := s.be.CopySnapData(v2, v1, progress.Null) c.Assert(err, IsNil) - err = s.be.CopySnapData(v2, v1, &s.nullProgress) + err = s.be.CopySnapData(v2, v1, progress.Null) c.Assert(err, IsNil) v2data := filepath.Join(dirs.SnapDataDir, "hello/20") @@ -364,15 +364,15 @@ v2 := snaptest.MockSnap(c, helloYaml2, helloContents, &snap.SideInfo{Revision: snap.R(20)}) // copy data - err := s.be.CopySnapData(v2, v1, &s.nullProgress) + err := s.be.CopySnapData(v2, v1, progress.Null) c.Assert(err, IsNil) v2data := filepath.Join(dirs.SnapDataDir, "hello/20") - err = s.be.UndoCopySnapData(v2, v1, &s.nullProgress) + err = s.be.UndoCopySnapData(v2, v1, progress.Null) c.Assert(err, IsNil) - err = s.be.UndoCopySnapData(v2, v1, &s.nullProgress) + err = s.be.UndoCopySnapData(v2, v1, progress.Null) c.Assert(err, IsNil) // now removed @@ -387,10 +387,10 @@ v1 := snaptest.MockSnap(c, helloYaml1, helloContents, &snap.SideInfo{Revision: snap.R(10)}) // first install - err := s.be.CopySnapData(v1, nil, &s.nullProgress) + err := s.be.CopySnapData(v1, nil, progress.Null) c.Assert(err, IsNil) - err = s.be.CopySnapData(v1, nil, &s.nullProgress) + err = s.be.CopySnapData(v1, nil, progress.Null) c.Assert(err, IsNil) _, err = os.Stat(v1.DataDir()) @@ -398,7 +398,7 @@ _, err = os.Stat(v1.CommonDataDir()) c.Assert(err, IsNil) - err = s.be.UndoCopySnapData(v1, nil, &s.nullProgress) + err = s.be.UndoCopySnapData(v1, nil, progress.Null) c.Assert(err, IsNil) _, err = os.Stat(v1.DataDir()) c.Check(os.IsNotExist(err), Equals, true) @@ -410,17 +410,17 @@ v1 := snaptest.MockSnap(c, helloYaml1, helloContents, &snap.SideInfo{Revision: snap.R(10)}) // first install - err := s.be.CopySnapData(v1, nil, &s.nullProgress) + err := s.be.CopySnapData(v1, nil, progress.Null) c.Assert(err, IsNil) _, err = os.Stat(v1.DataDir()) c.Assert(err, IsNil) _, err = os.Stat(v1.CommonDataDir()) c.Assert(err, IsNil) - err = s.be.UndoCopySnapData(v1, nil, &s.nullProgress) + err = s.be.UndoCopySnapData(v1, nil, progress.Null) c.Assert(err, IsNil) - err = s.be.UndoCopySnapData(v1, nil, &s.nullProgress) + err = s.be.UndoCopySnapData(v1, nil, progress.Null) c.Assert(err, IsNil) _, err = os.Stat(v1.DataDir()) @@ -443,7 +443,7 @@ } // copy data will fail - err := s.be.CopySnapData(v2, v1, &s.nullProgress) + err := s.be.CopySnapData(v2, v1, progress.Null) c.Assert(err, ErrorMatches, fmt.Sprintf(`cannot copy %s to %s: .*: "cp: boom" \(3\)`, q(v1.DataDir()), q(v2.DataDir()))) } @@ -466,7 +466,7 @@ c.Assert(os.Chmod(filepath.Join(homedir2, "hello", "10", "canary.home"), 0), IsNil) // try to copy data - err := s.be.CopySnapData(v2, v1, &s.nullProgress) + err := s.be.CopySnapData(v2, v1, progress.Null) c.Assert(err, NotNil) // the copy data failed, so check it cleaned up after itself (but not too much!) @@ -497,7 +497,7 @@ } // copy data works - err := s.be.CopySnapData(v1, v1, &s.nullProgress) + err := s.be.CopySnapData(v1, v1, progress.Null) c.Assert(err, IsNil) // the data is still there :-) @@ -533,7 +533,7 @@ } // undo copy data works - err := s.be.UndoCopySnapData(v1, v1, &s.nullProgress) + err := s.be.UndoCopySnapData(v1, v1, progress.Null) c.Assert(err, IsNil) // the data is still there :-) diff -Nru snapd-2.28.5/overlord/snapstate/backend/link.go snapd-2.29.3/overlord/snapstate/backend/link.go --- snapd-2.28.5/overlord/snapstate/backend/link.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/backend/link.go 2017-10-23 06:17:27.000000000 +0000 @@ -88,13 +88,13 @@ return err } // add the daemons from the snap.yaml - if err := wrappers.AddSnapServices(s, &progress.NullProgress{}); err != nil { + if err := wrappers.AddSnapServices(s, progress.Null); err != nil { wrappers.RemoveSnapBinaries(s) return err } // add the desktop files if err := wrappers.AddSnapDesktopFiles(s); err != nil { - wrappers.RemoveSnapServices(s, &progress.NullProgress{}) + wrappers.RemoveSnapServices(s, progress.Null) wrappers.RemoveSnapBinaries(s) return err } diff -Nru snapd-2.28.5/overlord/snapstate/backend/link_test.go snapd-2.29.3/overlord/snapstate/backend/link_test.go --- snapd-2.28.5/overlord/snapstate/backend/link_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/backend/link_test.go 2017-11-02 19:49:21.000000000 +0000 @@ -38,9 +38,9 @@ ) type linkSuite struct { - be backend.Backend - nullProgress progress.NullProgress - prevctlCmd func(...string) ([]byte, error) + be backend.Backend + + systemctlRestorer func() } var _ = Suite(&linkSuite{}) @@ -48,15 +48,14 @@ func (s *linkSuite) SetUpTest(c *C) { dirs.SetRootDir(c.MkDir()) - s.prevctlCmd = systemd.SystemctlCmd - systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) { + s.systemctlRestorer = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { return []byte("ActiveState=inactive\n"), nil - } + }) } func (s *linkSuite) TearDownTest(c *C) { dirs.SetRootDir("") - systemd.SystemctlCmd = s.prevctlCmd + s.systemctlRestorer() } func (s *linkSuite) TestLinkDoUndoGenerateWrappers(c *C) { @@ -87,7 +86,7 @@ c.Assert(l, HasLen, 1) // undo will remove - err = s.be.UnlinkSnap(info, &s.nullProgress) + err = s.be.UnlinkSnap(info, progress.Null) c.Assert(err, IsNil) l, err = filepath.Glob(filepath.Join(dirs.SnapBinariesDir, "*")) @@ -122,7 +121,7 @@ c.Assert(currentDataDir, Equals, dataDir) // undo will remove the symlinks - err = s.be.UnlinkSnap(info, &s.nullProgress) + err = s.be.UnlinkSnap(info, progress.Null) c.Assert(err, IsNil) c.Check(osutil.FileExists(currentActiveSymlink), Equals, false) @@ -193,10 +192,10 @@ err := s.be.LinkSnap(info) c.Assert(err, IsNil) - err = s.be.UnlinkSnap(info, &s.nullProgress) + err = s.be.UnlinkSnap(info, progress.Null) c.Assert(err, IsNil) - err = s.be.UnlinkSnap(info, &s.nullProgress) + err = s.be.UnlinkSnap(info, progress.Null) c.Assert(err, IsNil) // no wrappers @@ -257,9 +256,10 @@ Exec=bin `), 0644), IsNil) - systemd.SystemctlCmd = func(...string) ([]byte, error) { + r := systemd.MockSystemctl(func(...string) ([]byte, error) { return nil, nil - } + }) + defer r() // sanity checks for _, d := range []string{dirs.SnapBinariesDir, dirs.SnapDesktopFilesDir, dirs.SnapServicesDir} { @@ -300,9 +300,11 @@ } func (s *linkCleanupSuite) TestLinkCleanupOnSystemctlFail(c *C) { - systemd.SystemctlCmd = func(...string) ([]byte, error) { + r := systemd.MockSystemctl(func(...string) ([]byte, error) { return nil, errors.New("ouchie") - } + }) + defer r() + err := s.be.LinkSnap(s.info) c.Assert(err, ErrorMatches, "ouchie") diff -Nru snapd-2.28.5/overlord/snapstate/backend/mountunit_test.go snapd-2.29.3/overlord/snapstate/backend/mountunit_test.go --- snapd-2.28.5/overlord/snapstate/backend/mountunit_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/backend/mountunit_test.go 2017-10-27 06:23:41.000000000 +0000 @@ -37,9 +37,9 @@ ) type mountunitSuite struct { - nullProgress progress.NullProgress - prevctlCmd func(...string) ([]byte, error) - umount *testutil.MockCmd + umount *testutil.MockCmd + + systemctlRestorer func() } var _ = Suite(&mountunitSuite{}) @@ -50,17 +50,16 @@ err := os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "etc", "systemd", "system", "multi-user.target.wants"), 0755) c.Assert(err, IsNil) - s.prevctlCmd = systemd.SystemctlCmd - systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) { + s.systemctlRestorer = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { return []byte("ActiveState=inactive\n"), nil - } + }) s.umount = testutil.MockCommand(c, "umount", "") } func (s *mountunitSuite) TearDownTest(c *C) { dirs.SetRootDir("") - systemd.SystemctlCmd = s.prevctlCmd s.umount.Restore() + s.systemctlRestorer() } func (s *mountunitSuite) TestAddMountUnit(c *C) { @@ -72,7 +71,7 @@ Version: "1.1", Architectures: []string{"all"}, } - err := backend.AddMountUnit(info, &s.nullProgress) + err := backend.AddMountUnit(info, progress.Null) c.Assert(err, IsNil) // ensure correct mount unit @@ -81,6 +80,7 @@ c.Assert(err, IsNil) c.Assert(string(mount), Equals, fmt.Sprintf(`[Unit] Description=Mount unit for foo +Before=snapd.service [Mount] What=/var/lib/snapd/snaps/foo_13.snap @@ -104,7 +104,7 @@ Architectures: []string{"all"}, } - err := backend.AddMountUnit(info, &s.nullProgress) + err := backend.AddMountUnit(info, progress.Null) c.Assert(err, IsNil) // ensure we have the files @@ -113,7 +113,7 @@ c.Assert(osutil.FileExists(p), Equals, true) // now call remove and ensure they are gone - err = backend.RemoveMountUnit(info.MountDir(), &s.nullProgress) + err = backend.RemoveMountUnit(info.MountDir(), progress.Null) c.Assert(err, IsNil) p = filepath.Join(dirs.SnapServicesDir, un) c.Assert(osutil.FileExists(p), Equals, false) diff -Nru snapd-2.28.5/overlord/snapstate/backend/setup_test.go snapd-2.29.3/overlord/snapstate/backend/setup_test.go --- snapd-2.28.5/overlord/snapstate/backend/setup_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/backend/setup_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -40,10 +40,9 @@ ) type setupSuite struct { - be backend.Backend - nullProgress progress.NullProgress - prevctlCmd func(...string) ([]byte, error) - umount *testutil.MockCmd + be backend.Backend + umount *testutil.MockCmd + systemctlRestorer func() } var _ = Suite(&setupSuite{}) @@ -54,18 +53,18 @@ err := os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "etc", "systemd", "system", "multi-user.target.wants"), 0755) c.Assert(err, IsNil) - s.prevctlCmd = systemd.SystemctlCmd - systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) { + s.systemctlRestorer = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { return []byte("ActiveState=inactive\n"), nil - } + }) + s.umount = testutil.MockCommand(c, "umount", "") } func (s *setupSuite) TearDownTest(c *C) { dirs.SetRootDir("") partition.ForceBootloader(nil) - systemd.SystemctlCmd = s.prevctlCmd s.umount.Restore() + s.systemctlRestorer() } func (s *setupSuite) TestSetupDoUndoSimple(c *C) { @@ -76,7 +75,7 @@ Revision: snap.R(14), } - err := s.be.SetupSnap(snapPath, &si, &s.nullProgress) + err := s.be.SetupSnap(snapPath, &si, progress.Null) c.Assert(err, IsNil) // after setup the snap file is in the right dir @@ -94,7 +93,7 @@ c.Assert(osutil.FileExists(minInfo.MountDir()), Equals, true) // undo undoes the mount unit and the instdir creation - err = s.be.UndoSetupSnap(minInfo, "app", &s.nullProgress) + err = s.be.UndoSetupSnap(minInfo, "app", progress.Null) c.Assert(err, IsNil) l, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "*.mount")) @@ -129,7 +128,7 @@ Revision: snap.R(140), } - err := s.be.SetupSnap(snapPath, &si, &s.nullProgress) + err := s.be.SetupSnap(snapPath, &si, progress.Null) c.Assert(err, IsNil) l, _ := filepath.Glob(filepath.Join(bootloader.Dir(), "*")) c.Assert(l, HasLen, 1) @@ -137,7 +136,7 @@ minInfo := snap.MinimalPlaceInfo("kernel", snap.R(140)) // undo deletes the kernel assets again - err = s.be.UndoSetupSnap(minInfo, "kernel", &s.nullProgress) + err = s.be.UndoSetupSnap(minInfo, "kernel", progress.Null) c.Assert(err, IsNil) l, _ = filepath.Glob(filepath.Join(bootloader.Dir(), "*")) @@ -173,11 +172,11 @@ Revision: snap.R(140), } - err := s.be.SetupSnap(snapPath, &si, &s.nullProgress) + err := s.be.SetupSnap(snapPath, &si, progress.Null) c.Assert(err, IsNil) // retry run - err = s.be.SetupSnap(snapPath, &si, &s.nullProgress) + err = s.be.SetupSnap(snapPath, &si, progress.Null) c.Assert(err, IsNil) minInfo := snap.MinimalPlaceInfo("kernel", snap.R(140)) @@ -222,16 +221,16 @@ Revision: snap.R(140), } - err := s.be.SetupSnap(snapPath, &si, &s.nullProgress) + err := s.be.SetupSnap(snapPath, &si, progress.Null) c.Assert(err, IsNil) minInfo := snap.MinimalPlaceInfo("kernel", snap.R(140)) - err = s.be.UndoSetupSnap(minInfo, "kernel", &s.nullProgress) + err = s.be.UndoSetupSnap(minInfo, "kernel", progress.Null) c.Assert(err, IsNil) // retry run - err = s.be.UndoSetupSnap(minInfo, "kernel", &s.nullProgress) + err = s.be.UndoSetupSnap(minInfo, "kernel", progress.Null) c.Assert(err, IsNil) // sanity checks diff -Nru snapd-2.28.5/overlord/snapstate/backend/snapdata.go snapd-2.29.3/overlord/snapstate/backend/snapdata.go --- snapd-2.28.5/overlord/snapstate/backend/snapdata.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/backend/snapdata.go 2017-10-23 06:17:27.000000000 +0000 @@ -26,6 +26,7 @@ "path/filepath" unix "syscall" + "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/snap" @@ -86,6 +87,8 @@ if err != nil { return nil, err } + // then the /root user (including GlobalRootDir for tests) + found = append(found, snap.UserDataDir(filepath.Join(dirs.GlobalRootDir, "/root/"))) // then system data found = append(found, snap.DataDir()) diff -Nru snapd-2.28.5/overlord/snapstate/backend.go snapd-2.29.3/overlord/snapstate/backend.go --- snapd-2.28.5/overlord/snapstate/backend.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/backend.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,32 +20,11 @@ package snapstate import ( - "github.com/snapcore/snapd/asserts" - "github.com/snapcore/snapd/overlord/auth" "github.com/snapcore/snapd/overlord/snapstate/backend" "github.com/snapcore/snapd/progress" "github.com/snapcore/snapd/snap" - "github.com/snapcore/snapd/store" - - "golang.org/x/net/context" ) -// A StoreService can find, list available updates and download snaps. -type StoreService interface { - SnapInfo(spec store.SnapSpec, user *auth.UserState) (*snap.Info, error) - Find(search *store.Search, user *auth.UserState) ([]*snap.Info, error) - LookupRefresh(*store.RefreshCandidate, *auth.UserState) (*snap.Info, error) - ListRefresh([]*store.RefreshCandidate, *auth.UserState) ([]*snap.Info, error) - Sections(user *auth.UserState) ([]string, error) - Download(context.Context, string, string, *snap.DownloadInfo, progress.Meter, *auth.UserState) error - - Assertion(assertType *asserts.AssertionType, primaryKey []string, user *auth.UserState) (asserts.Assertion, error) - - SuggestedCurrency() string - Buy(options *store.BuyOptions, user *auth.UserState) (*store.BuyResult, error) - ReadyToBuy(*auth.UserState) error -} - type managerBackend interface { // install releated SetupSnap(snapFilePath string, si *snap.SideInfo, meter progress.Meter) error diff -Nru snapd-2.28.5/overlord/snapstate/backend_test.go snapd-2.29.3/overlord/snapstate/backend_test.go --- snapd-2.28.5/overlord/snapstate/backend_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/backend_test.go 2017-11-02 15:44:20.000000000 +0000 @@ -22,6 +22,7 @@ import ( "errors" "fmt" + "io" "sort" "strings" @@ -93,6 +94,7 @@ storetest.Store downloads []fakeDownload + refreshRevnos map[string]snap.Revision fakeBackend *fakeSnappyBackend fakeCurrentProgress int fakeTotalProgress int @@ -176,6 +178,9 @@ } revno := snap.R(11) + if r := f.refreshRevnos[cand.SnapID]; !r.Unset() { + revno = r + } confinement := snap.StrictConfinement switch cand.Channel { case "channel-for-7": @@ -269,6 +274,24 @@ return nil } +func (f *fakeStore) WriteCatalogs(io.Writer) error { + f.pokeStateLock() + f.fakeBackend.ops = append(f.fakeBackend.ops, fakeOp{ + op: "x-commands", + }) + + return nil +} + +func (f *fakeStore) Sections(*auth.UserState) ([]string, error) { + f.pokeStateLock() + f.fakeBackend.ops = append(f.fakeBackend.ops, fakeOp{ + op: "x-sections", + }) + + return nil, nil +} + type fakeSnappyBackend struct { ops fakeOps diff -Nru snapd-2.28.5/overlord/snapstate/booted_test.go snapd-2.29.3/overlord/snapstate/booted_test.go --- snapd-2.28.5/overlord/snapstate/booted_test.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/booted_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -24,34 +24,44 @@ import ( "os" "path/filepath" + "time" . "gopkg.in/check.v1" "github.com/snapcore/snapd/boot/boottest" "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/overlord" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" "github.com/snapcore/snapd/partition" "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/snap/snaptest" + "github.com/snapcore/snapd/testutil" ) type bootedSuite struct { + testutil.BaseTest bootloader *boottest.MockBootloader + o *overlord.Overlord state *state.State snapmgr *snapstate.SnapManager fakeBackend *fakeSnappyBackend + restore func() } var _ = Suite(&bootedSuite{}) func (bs *bootedSuite) SetUpTest(c *C) { + bs.BaseTest.SetUpTest(c) + dirs.SetRootDir(c.MkDir()) err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755) c.Assert(err, IsNil) + bs.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) + // booted is not running on classic release.MockOnClassic(false) @@ -61,11 +71,14 @@ partition.ForceBootloader(bs.bootloader) bs.fakeBackend = &fakeSnappyBackend{} - bs.state = state.New(nil) + bs.o = overlord.Mock() + bs.state = bs.o.State() bs.snapmgr, err = snapstate.Manager(bs.state) c.Assert(err, IsNil) bs.snapmgr.AddForeignTaskHandlers(bs.fakeBackend) + bs.o.AddManager(bs.snapmgr) + snapstate.SetSnapManagerBackend(bs.snapmgr, bs.fakeBackend) snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) { return nil, nil @@ -73,6 +86,7 @@ } func (bs *bootedSuite) TearDownTest(c *C) { + bs.BaseTest.TearDownTest(c) snapstate.AutoAliases = nil release.MockOnClassic(true) dirs.SetRootDir("") @@ -85,10 +99,7 @@ var kernelSI2 = &snap.SideInfo{RealName: "canonical-pc-linux", Revision: snap.R(2)} func (bs *bootedSuite) settle() { - for i := 0; i < 50; i++ { - bs.snapmgr.Ensure() - bs.snapmgr.Wait() - } + bs.o.Settle(5 * time.Second) } func (bs *bootedSuite) makeInstalledKernelOS(c *C, st *state.State) { diff -Nru snapd-2.28.5/overlord/snapstate/check_snap_test.go snapd-2.29.3/overlord/snapstate/check_snap_test.go --- snapd-2.28.5/overlord/snapstate/check_snap_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/check_snap_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -32,22 +32,27 @@ "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/snap/snaptest" + "github.com/snapcore/snapd/testutil" "github.com/snapcore/snapd/overlord/snapstate" ) type checkSnapSuite struct { + testutil.BaseTest st *state.State } var _ = Suite(&checkSnapSuite{}) func (s *checkSnapSuite) SetUpTest(c *C) { + s.BaseTest.SetUpTest(c) dirs.SetRootDir(c.MkDir()) s.st = state.New(nil) + s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) } func (s *checkSnapSuite) TearDownTest(c *C) { + s.BaseTest.TearDownTest(c) dirs.SetRootDir("") } diff -Nru snapd-2.28.5/overlord/snapstate/export_test.go snapd-2.29.3/overlord/snapstate/export_test.go --- snapd-2.28.5/overlord/snapstate/export_test.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -21,6 +21,7 @@ import ( "errors" + "time" "gopkg.in/tomb.v2" @@ -95,11 +96,16 @@ return func() { errtrackerReport = prev } } +func MockPrerequisitesRetryTimeout(d time.Duration) (restore func()) { + old := prerequisitesRetryTimeout + prerequisitesRetryTimeout = d + return func() { prerequisitesRetryTimeout = old } +} + var ( CheckSnap = checkSnap CanRemove = canRemove CanDisable = canDisable - CachedStore = cachedStore DefaultRefreshSchedule = defaultRefreshSchedule NameAndRevnoFromSnap = nameAndRevnoFromSnap ) diff -Nru snapd-2.28.5/overlord/snapstate/flags.go snapd-2.29.3/overlord/snapstate/flags.go --- snapd-2.28.5/overlord/snapstate/flags.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/flags.go 2017-11-02 15:44:20.000000000 +0000 @@ -62,7 +62,6 @@ // ForSnapSetup returns a copy of the Flags with the flags that we don't need in SnapSetup set to false (so they're not serialized) func (f Flags) ForSnapSetup() Flags { - f.IgnoreValidation = false f.SkipConfigure = false return f } diff -Nru snapd-2.28.5/overlord/snapstate/handlers_download_test.go snapd-2.29.3/overlord/snapstate/handlers_download_test.go --- snapd-2.28.5/overlord/snapstate/handlers_download_test.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/handlers_download_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -24,6 +24,7 @@ "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" "github.com/snapcore/snapd/snap" ) @@ -49,7 +50,7 @@ state: s.state, fakeBackend: s.fakeBackend, } - snapstate.ReplaceStore(s.state, s.fakeStore) + storestate.ReplaceStore(s.state, s.fakeStore) var err error s.snapmgr, err = snapstate.Manager(s.state) diff -Nru snapd-2.28.5/overlord/snapstate/handlers.go snapd-2.29.3/overlord/snapstate/handlers.go --- snapd-2.28.5/overlord/snapstate/handlers.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/handlers.go 2017-11-02 15:44:20.000000000 +0000 @@ -36,6 +36,7 @@ "github.com/snapcore/snapd/overlord/configstate/config" "github.com/snapcore/snapd/overlord/snapstate/backend" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/store" @@ -115,6 +116,107 @@ */ +const defaultCoreSnapName = "core" +const defaultBaseSnapsChannel = "stable" + +func changeInFlight(st *state.State, snapName string) (bool, error) { + for _, chg := range st.Changes() { + if chg.Status().Ready() { + continue + } + for _, tc := range chg.Tasks() { + if tc.Kind() == "link-snap" || tc.Kind() == "discard-snap" { + snapsup, err := TaskSnapSetup(tc) + if err != nil { + return false, err + } + // some other change aleady inflight + if snapsup.Name() == snapName { + return true, nil + } + } + } + } + + return false, nil +} + +// timeout for tasks to check if the prerequisites are ready +var prerequisitesRetryTimeout = 30 * time.Second + +func (m *SnapManager) doPrerequisites(t *state.Task, _ *tomb.Tomb) error { + st := t.State() + st.Lock() + defer st.Unlock() + + // check if we need to inject tasks to install core + snapsup, _, err := snapSetupAndState(t) + if err != nil { + return err + } + + // core/ubuntu-core can not have prerequisites + snapName := snapsup.Name() + if snapName == defaultCoreSnapName || snapName == "ubuntu-core" { + return nil + } + + // check prereqs + prereqName := defaultCoreSnapName + if snapsup.Base != "" { + prereqName = snapsup.Base + } + + var prereqState SnapState + err = Get(st, prereqName, &prereqState) + // we have the prereq already + if err == nil { + return nil + } + // if it is a real error, report + if err != state.ErrNoState { + return err + } + + // check that there is no task that installs the prereq already + prereqPending, err := changeInFlight(st, prereqName) + if err != nil { + return err + } + if prereqPending { + // if something else installs core already we need to + // wait for that to either finish successfully or fail + return &state.Retry{After: prerequisitesRetryTimeout} + } + + // not installed, nor queued for install -> install it + ts, err := Install(st, prereqName, defaultBaseSnapsChannel, snap.R(0), snapsup.UserID, Flags{}) + // something might have triggered an explicit install of core while + // the state was unlocked -> deal with that here + if _, ok := err.(changeDuringInstallError); ok { + return &state.Retry{After: prerequisitesRetryTimeout} + } + if _, ok := err.(changeConflictError); ok { + return &state.Retry{After: prerequisitesRetryTimeout} + } + if err != nil { + return err + } + ts.JoinLane(st.NewLane()) + + // inject install for core into this change + chg := t.Change() + for _, t := range chg.Tasks() { + t.WaitAll(ts) + } + chg.AddAll(ts) + // make sure that the new change is committed to the state + // together with marking this task done + t.SetStatus(state.DoneStatus) + + return nil +} + func (m *SnapManager) doPrepareSnap(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() @@ -223,7 +325,7 @@ } st.Lock() - theStore := Store(st) + theStore := storestate.Store(st) user, err := userFromUserID(st, snapsup.UserID) st.Unlock() if err != nil { @@ -295,6 +397,7 @@ if err != nil { return err } + t.State().Lock() t.Set("snap-type", newInfo.Type) t.State().Unlock() @@ -541,6 +644,8 @@ if snapsup.Channel != "" { snapst.Channel = snapsup.Channel } + oldIgnoreValidation := snapst.IgnoreValidation + snapst.IgnoreValidation = snapsup.IgnoreValidation oldTryMode := snapst.TryMode snapst.TryMode = snapsup.TryMode oldDevMode := snapst.DevMode @@ -592,6 +697,7 @@ t.Set("old-devmode", oldDevMode) t.Set("old-jailmode", oldJailMode) t.Set("old-classic", oldClassic) + t.Set("old-ignore-validation", oldIgnoreValidation) t.Set("old-channel", oldChannel) t.Set("old-current", oldCurrent) t.Set("old-candidate-index", oldCandidateIndex) @@ -636,6 +742,11 @@ if err != nil { return err } + var oldIgnoreValidation bool + err = t.Get("old-ignore-validation", &oldIgnoreValidation) + if err != nil && err != state.ErrNoState { + return err + } var oldTryMode bool err = t.Get("old-trymode", &oldTryMode) if err != nil { @@ -690,6 +801,7 @@ snapst.Current = oldCurrent snapst.Active = false snapst.Channel = oldChannel + snapst.IgnoreValidation = oldIgnoreValidation snapst.TryMode = oldTryMode snapst.DevMode = oldDevMode snapst.JailMode = oldJailMode diff -Nru snapd-2.28.5/overlord/snapstate/snapmgr.go snapd-2.29.3/overlord/snapstate/snapmgr.go --- snapd-2.28.5/overlord/snapstate/snapmgr.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/snapmgr.go 2017-10-23 06:17:27.000000000 +0000 @@ -35,9 +35,9 @@ "github.com/snapcore/snapd/overlord/configstate/config" "github.com/snapcore/snapd/overlord/snapstate/backend" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" - "github.com/snapcore/snapd/store" "github.com/snapcore/snapd/strutil" "github.com/snapcore/snapd/timeutil" ) @@ -69,6 +69,7 @@ // overridden in the tests var errtrackerReport = errtracker.Report +var catalogRefreshDelay = 24 * time.Hour // SnapManager is responsible for the installation and removal of snaps. type SnapManager struct { @@ -79,6 +80,8 @@ nextRefresh time.Time lastRefreshAttempt time.Time + nextCatalogRefresh time.Time + lastUbuntuCoreTransitionAttempt time.Time runner *state.TaskRunner @@ -89,6 +92,7 @@ // FIXME: rename to RequestedChannel to convey the meaning better Channel string `json:"channel,omitempty"` UserID int `json:"user-id,omitempty"` + Base string `json:"base,omitempty"` Flags @@ -266,32 +270,6 @@ return false } -type cachedStoreKey struct{} - -// ReplaceStore replaces the store used by the manager. -func ReplaceStore(state *state.State, store StoreService) { - state.Cache(cachedStoreKey{}, store) -} - -func cachedStore(st *state.State) StoreService { - ubuntuStore := st.Cached(cachedStoreKey{}) - if ubuntuStore == nil { - return nil - } - return ubuntuStore.(StoreService) -} - -// the store implementation has the interface consumed here -var _ StoreService = (*store.Store)(nil) - -// Store returns the store service used by the snapstate package. -func Store(st *state.State) StoreService { - if cachedStore := cachedStore(st); cachedStore != nil { - return cachedStore - } - panic("internal error: needing the store before managers have initialized it") -} - // Manager returns a new snap manager. func Manager(st *state.State) (*SnapManager, error) { runner := state.NewTaskRunner(st) @@ -312,6 +290,10 @@ }, nil) // install/update related + + // TODO: no undo handler here, we may use the GC for this and just + // remove anything that is not referenced anymore + runner.AddHandler("prerequisites", m.doPrerequisites, nil) runner.AddHandler("prepare-snap", m.doPrepareSnap, m.undoPrepareSnap) runner.AddHandler("download-snap", m.doDownloadSnap, m.undoPrepareSnap) runner.AddHandler("mount-snap", m.doMountSnap, m.undoMountSnap) @@ -351,18 +333,21 @@ // control serialisation runner.SetBlocked(m.blockedTask) - // test handlers - runner.AddHandler("fake-install-snap", func(t *state.Task, _ *tomb.Tomb) error { - return nil - }, nil) - runner.AddHandler("fake-install-snap-error", func(t *state.Task, _ *tomb.Tomb) error { - return fmt.Errorf("fake-install-snap-error errored") - }, nil) - return m, nil } func (m *SnapManager) blockedTask(cand *state.Task, running []*state.Task) bool { + // Serialize "prerequisites", the state lock is not enough as + // Install() inside doPrerequisites() will unlock to talk to + // the store. + if cand.Kind() == "prerequisites" { + for _, t := range running { + if t.Kind() == "prerequisites" { + return true + } + } + } + return false } @@ -468,14 +453,26 @@ return lastRefresh, nil } +// NextRefresh returns the time the next update of the system's snaps +// will be attempted. +// The caller should be holding the state lock. func (m *SnapManager) NextRefresh() time.Time { return m.nextRefresh } +// RefreshSchedule returns the current refresh schedule. +// The caller should be holding the state lock. func (m *SnapManager) RefreshSchedule() string { return m.currentRefreshSchedule } +// NextCatalogRefresh returns the time the next update of catalog +// data will be attempted. +// The caller should be holding the state lock. +func (m *SnapManager) NextCatalogRefresh() time.Time { + return m.nextCatalogRefresh +} + // ensureRefreshes ensures that we refresh all installed snaps periodically func (m *SnapManager) ensureRefreshes() error { m.state.Lock() @@ -538,6 +535,33 @@ return err } +// ensureCatalogRefresh ensures that we refresh the catalog +// data periodically +func (m *SnapManager) ensureCatalogRefresh() error { + // sneakily don't do anything if in testing + if CanAutoRefresh == nil { + return nil + } + m.state.Lock() + defer m.state.Unlock() + + theStore := storestate.Store(m.state) + now := time.Now() + needsRefresh := m.nextCatalogRefresh.IsZero() || m.nextCatalogRefresh.Before(now) + + if !needsRefresh { + return nil + } + + next := now.Add(catalogRefreshDelay) + // catalog refresh does not carry on trying on error + m.nextCatalogRefresh = next + + logger.Debugf("Catalog refresh starting now; next scheduled for %s.", next) + + return refreshCatalogs(m.state, theStore) +} + // ensureForceDevmodeDropsDevmodeFromState undoes the froced devmode // in snapstate for forced devmode distros. func (m *SnapManager) ensureForceDevmodeDropsDevmodeFromState() error { @@ -687,6 +711,7 @@ m.ensureForceDevmodeDropsDevmodeFromState(), m.ensureUbuntuCoreTransition(), m.ensureRefreshes(), + m.ensureCatalogRefresh(), } m.runner.Ensure() diff -Nru snapd-2.28.5/overlord/snapstate/snapstate.go snapd-2.29.3/overlord/snapstate/snapstate.go --- snapd-2.28.5/overlord/snapstate/snapstate.go 2017-10-04 14:43:22.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/snapstate.go 2017-11-02 15:44:20.000000000 +0000 @@ -23,16 +23,21 @@ import ( "encoding/json" "fmt" + "os" "reflect" "sort" + "strings" "github.com/snapcore/snapd/boot" "github.com/snapcore/snapd/dirs" - "github.com/snapcore/snapd/i18n/dumb" + "github.com/snapcore/snapd/i18n" "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/logger" + "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/overlord/auth" + "github.com/snapcore/snapd/overlord/snapstate/backend" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/store" @@ -78,6 +83,10 @@ return nil, err } + // ensure core gets installed. if it is already installed return + // an empty task set + ts := state.NewTaskSet() + targetRevision := snapsup.Revision() revisionStr := "" if snapsup.SideInfo != nil { @@ -87,6 +96,9 @@ // check if we already have the revision locally (alters tasks) revisionIsLocal := snapst.LastIndex(targetRevision) >= 0 + prereq := st.NewTask("prerequisites", fmt.Sprintf(i18n.G("Ensure prerequisites for %q are available"), snapsup.Name())) + prereq.Set("snap-setup", snapsup) + var prepare, prev *state.Task fromStore := false // if we have a local revision here we go back to that @@ -97,8 +109,9 @@ prepare = st.NewTask("download-snap", fmt.Sprintf(i18n.G("Download snap %q%s from channel %q"), snapsup.Name(), revisionStr, snapsup.Channel)) } prepare.Set("snap-setup", snapsup) + prepare.WaitFor(prereq) - tasks := []*state.Task{prepare} + tasks := []*state.Task{prereq, prepare} addTask := func(t *state.Task) { t.Set("snap-setup-task", prepare.ID()) t.WaitFor(prev) @@ -236,6 +249,8 @@ } installSet := state.NewTaskSet(tasks...) + installSet.WaitAll(ts) + ts.AddAll(installSet) if flags&skipConfigure != 0 { return installSet, nil @@ -249,10 +264,10 @@ } configSet := ConfigureSnap(st, snapsup.Name(), confFlags) - configSet.WaitAll(installSet) - installSet.AddAll(configSet) + configSet.WaitAll(ts) + ts.AddAll(configSet) - return installSet, nil + return ts, nil } // ConfigureSnap returns a set of tasks to configure snapName as done during installation/refresh. @@ -260,7 +275,7 @@ // This is slightly ugly, ideally we would check the type instead // of hardcoding the name here. Unfortunately we do not have the // type until we actually run the change. - if snapName == "core" { + if snapName == defaultCoreSnapName { confFlags |= IgnoreHookError confFlags |= TrackHookError } @@ -310,6 +325,14 @@ return &plugRef, &slotRef, nil } +type changeConflictError struct { + snapName string +} + +func (e changeConflictError) Error() string { + return fmt.Sprintf("snap %q has changes in progress", e.snapName) +} + // CheckChangeConflictMany ensures that for the given snapNames no other // changes that alters the snaps (like remove, install, refresh) are in // progress. If a conflict is detected an error is returned. @@ -347,7 +370,7 @@ } else { snapName = slotRef.Snap } - return fmt.Errorf("snap %q has changes in progress", snapName) + return changeConflictError{snapName} } } else { snapsup, err := TaskSnapSetup(task) @@ -356,7 +379,7 @@ } snapName := snapsup.Name() if (snapMap[snapName]) && (checkConflictPredicate == nil || checkConflictPredicate(k)) { - return fmt.Errorf("snap %q has changes in progress", snapName) + return changeConflictError{snapName} } } } @@ -365,6 +388,14 @@ return nil } +type changeDuringInstallError struct { + snapName string +} + +func (c changeDuringInstallError) Error() string { + return fmt.Sprintf("snap %q state changed during install preparations", c.snapName) +} + // CheckChangeConflict ensures that for the given snapName no other // changes that alters the snap (like remove, install, refresh) are in // progress. It also ensures that snapst (if not nil) did not get @@ -388,7 +419,7 @@ // TODO: implement the rather-boring-but-more-performant SnapState.Equals if !reflect.DeepEqual(snapst, &cursnapst) { - return fmt.Errorf("snap %q state changed during install preparations", snapName) + return changeDuringInstallError{snapName: snapName} } } @@ -425,7 +456,15 @@ instFlags |= skipConfigure } + // It is ok do open the snap file here because we either + // have side info or the user passed --dangerous + info, _, err := backend.OpenSnapFile(path, si) + if err != nil { + return nil, err + } + snapsup := &SnapSetup{ + Base: info.Base, SideInfo: si, SnapPath: path, Channel: channel, @@ -470,6 +509,7 @@ snapsup := &SnapSetup{ Channel: channel, + Base: info.Base, UserID: userID, Flags: flags.ForSnapSetup(), DownloadInfo: &info.DownloadInfo, @@ -503,20 +543,21 @@ // RefreshCandidates gets a list of candidates for update // Note that the state must be locked by the caller. func RefreshCandidates(st *state.State, user *auth.UserState) ([]*snap.Info, error) { - updates, _, err := refreshCandidates(st, nil, user) + updates, _, _, err := refreshCandidates(st, nil, user) return updates, err } -func refreshCandidates(st *state.State, names []string, user *auth.UserState) ([]*snap.Info, map[string]*SnapState, error) { +func refreshCandidates(st *state.State, names []string, user *auth.UserState) ([]*snap.Info, map[string]*SnapState, map[string]bool, error) { snapStates, err := All(st) if err != nil { - return nil, nil, err + return nil, nil, nil, err } sort.Strings(names) stateByID := make(map[string]*SnapState, len(snapStates)) candidatesInfo := make([]*store.RefreshCandidate, 0, len(snapStates)) + ignoreValidation := make(map[string]bool) for _, snapst := range snapStates { if len(names) == 0 && (snapst.TryMode || snapst.DevMode) { // no auto-refresh for trymode nor devmode @@ -549,10 +590,11 @@ // get confinement preference from the snapstate candidateInfo := &store.RefreshCandidate{ // the desired channel (not info.Channel!) - Channel: snapst.Channel, - SnapID: snapInfo.SnapID, - Revision: snapInfo.Revision, - Epoch: snapInfo.Epoch, + Channel: snapst.Channel, + SnapID: snapInfo.SnapID, + Revision: snapInfo.Revision, + Epoch: snapInfo.Epoch, + IgnoreValidation: snapst.IgnoreValidation, } if len(names) == 0 { @@ -560,22 +602,25 @@ } candidatesInfo = append(candidatesInfo, candidateInfo) + if snapst.IgnoreValidation { + ignoreValidation[snapInfo.SnapID] = true + } } - theStore := Store(st) + theStore := storestate.Store(st) st.Unlock() updates, err := theStore.ListRefresh(candidatesInfo, user) st.Lock() if err != nil { - return nil, nil, err + return nil, nil, nil, err } - return updates, stateByID, nil + return updates, stateByID, ignoreValidation, nil } // ValidateRefreshes allows to hook validation into the handling of refresh candidates. -var ValidateRefreshes func(st *state.State, refreshes []*snap.Info, userID int) (validated []*snap.Info, err error) +var ValidateRefreshes func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int) (validated []*snap.Info, err error) // UpdateMany updates everything from the given list of names that the // store says is updateable. If the list is empty, update everything. @@ -586,13 +631,13 @@ return nil, nil, err } - updates, stateByID, err := refreshCandidates(st, names, user) + updates, stateByID, ignoreValidation, err := refreshCandidates(st, names, user) if err != nil { return nil, nil, err } if ValidateRefreshes != nil && len(updates) != 0 { - updates, err = ValidateRefreshes(st, updates, userID) + updates, err = ValidateRefreshes(st, updates, ignoreValidation, userID) if err != nil { // not doing "refresh all" report the error if len(names) != 0 { @@ -863,6 +908,8 @@ channel = snapst.Channel } + // TODO: make flags be per revision to avoid this logic (that + // leaves corner cases all over the place) if !(flags.JailMode || flags.DevMode) { flags.Classic = flags.Classic || snapst.Flags.Classic } @@ -889,6 +936,7 @@ // see if we need to update the channel if infoErr == store.ErrNoUpdateAvailable && snapst.Channel != channel { + // TODO: do we want to treat ignore-validation similarly? snapsup := &SnapSetup{ SideInfo: snapst.CurrentSideInfo(), // update the tracked channel @@ -922,7 +970,7 @@ func infoForUpdate(st *state.State, snapst *SnapState, name, channel string, revision snap.Revision, userID int, flags Flags) (*snap.Info, error) { if revision.Unset() { // good ol' refresh - info, err := updateInfo(st, snapst, channel, userID) + info, err := updateInfo(st, snapst, channel, flags.IgnoreValidation, userID) if err != nil { return nil, err } @@ -930,7 +978,7 @@ return nil, err } if ValidateRefreshes != nil && !flags.IgnoreValidation { - _, err := ValidateRefreshes(st, []*snap.Info{info}, userID) + _, err := ValidateRefreshes(st, []*snap.Info{info}, nil, userID) if err != nil { return nil, err } @@ -1342,6 +1390,19 @@ return nil, err } flags.Revert = true + // TODO: make flags be per revision to avoid this logic (that + // leaves corner cases all over the place) + if !(flags.JailMode || flags.DevMode || flags.Classic) { + if snapst.Flags.DevMode { + flags.DevMode = true + } + if snapst.Flags.JailMode { + flags.JailMode = true + } + if snapst.Flags.Classic { + flags.Classic = true + } + } snapsup := &SnapSetup{ SideInfo: snapst.Sequence[i], Flags: flags.ForSnapSetup(), @@ -1628,10 +1689,10 @@ // some systems have two cores: ubuntu-core/core // we always return "core" in this case if len(res) == 2 { - if res[0].Name() == "core" && res[1].Name() == "ubuntu-core" { + if res[0].Name() == defaultCoreSnapName && res[1].Name() == "ubuntu-core" { return res[0], nil } - if res[0].Name() == "ubuntu-core" && res[1].Name() == "core" { + if res[0].Name() == "ubuntu-core" && res[1].Name() == defaultCoreSnapName { return res[1], nil } return nil, fmt.Errorf("unexpected cores %q and %q", res[0].Name(), res[1].Name()) @@ -1669,3 +1730,33 @@ return defaults, nil } + +func refreshCatalogs(st *state.State, theStore storestate.StoreService) error { + st.Unlock() + defer st.Lock() + + if err := os.MkdirAll(dirs.SnapCacheDir, 0755); err != nil { + return fmt.Errorf("cannot create directory %q: %v", dirs.SnapCacheDir, err) + } + + sections, err := theStore.Sections(nil) + if err != nil { + return err + } + + sort.Strings(sections) + if err := osutil.AtomicWriteFile(dirs.SnapSectionsFile, []byte(strings.Join(sections, "\n")), 0644, 0); err != nil { + return err + } + + namesFile, err := osutil.NewAtomicFile(dirs.SnapNamesFile, 0644, 0, -1, -1) + if err != nil { + return err + } + defer namesFile.Cancel() + if err := theStore.WriteCatalogs(namesFile); err != nil { + return err + } + + return namesFile.Commit() +} diff -Nru snapd-2.28.5/overlord/snapstate/snapstate_test.go snapd-2.29.3/overlord/snapstate/snapstate_test.go --- snapd-2.28.5/overlord/snapstate/snapstate_test.go 2017-10-04 14:43:22.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/snapstate_test.go 2017-11-02 15:44:20.000000000 +0000 @@ -20,7 +20,6 @@ package snapstate_test import ( - "bytes" "encoding/json" "errors" "fmt" @@ -37,12 +36,14 @@ "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" + "github.com/snapcore/snapd/overlord" "github.com/snapcore/snapd/overlord/auth" "github.com/snapcore/snapd/overlord/configstate/config" "github.com/snapcore/snapd/overlord/hookstate" "github.com/snapcore/snapd/overlord/snapstate" "github.com/snapcore/snapd/overlord/snapstate/backend" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/snap/snaptest" @@ -57,6 +58,8 @@ func TestSnapManager(t *testing.T) { TestingT(t) } type snapmgrTestSuite struct { + testutil.BaseTest + o *overlord.Overlord state *state.State snapmgr *snapstate.SnapManager @@ -64,25 +67,25 @@ fakeStore *fakeStore user *auth.UserState - - reset func() } -func (s *snapmgrTestSuite) settle() { - // FIXME: use the real settle here - for i := 0; i < 50; i++ { - s.snapmgr.Ensure() - s.snapmgr.Wait() - } +func (s *snapmgrTestSuite) settle(c *C) { + err := s.o.Settle(5 * time.Second) + c.Assert(err, IsNil) } var _ = Suite(&snapmgrTestSuite{}) func (s *snapmgrTestSuite) SetUpTest(c *C) { + s.BaseTest.SetUpTest(c) dirs.SetRootDir(c.MkDir()) + s.o = overlord.Mock() + s.state = s.o.State() + + s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) + s.fakeBackend = &fakeSnappyBackend{} - s.state = state.New(nil) s.fakeStore = &fakeStore{ fakeCurrentProgress: 75, fakeTotalProgress: 100, @@ -104,23 +107,32 @@ snapstate.SetSnapManagerBackend(s.snapmgr, s.fakeBackend) - restore1 := snapstate.MockReadInfo(s.fakeBackend.ReadInfo) - restore2 := snapstate.MockOpenSnapFile(s.fakeBackend.OpenSnapFile) + s.o.AddManager(s.snapmgr) + + s.BaseTest.AddCleanup(snapstate.MockReadInfo(s.fakeBackend.ReadInfo)) + s.BaseTest.AddCleanup(snapstate.MockOpenSnapFile(s.fakeBackend.OpenSnapFile)) - s.reset = func() { + s.BaseTest.AddCleanup(func() { snapstate.SetupInstallHook = oldSetupInstallHook snapstate.SetupPostRefreshHook = oldSetupPostRefreshHook snapstate.SetupRemoveHook = oldSetupRemoveHook - restore2() - restore1() dirs.SetRootDir("/") - } + }) s.state.Lock() - snapstate.ReplaceStore(s.state, s.fakeStore) + storestate.ReplaceStore(s.state, s.fakeStore) s.user, err = auth.NewUser(s.state, "username", "email@test.com", "macaroon", []string{"discharge"}) c.Assert(err, IsNil) + + snapstate.Set(s.state, "core", &snapstate.SnapState{ + Active: true, + Sequence: []*snap.SideInfo{ + {RealName: "core", Revision: snap.R(1)}, + }, + Current: snap.R(1), + SnapType: "os", + }) s.state.Unlock() snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) { @@ -129,24 +141,10 @@ } func (s *snapmgrTestSuite) TearDownTest(c *C) { + s.BaseTest.TearDownTest(c) snapstate.ValidateRefreshes = nil snapstate.AutoAliases = nil snapstate.CanAutoRefresh = nil - s.reset() -} - -func (s *snapmgrTestSuite) TestStore(c *C) { - s.state.Lock() - defer s.state.Unlock() - - sto := &store.Store{} - snapstate.ReplaceStore(s.state, sto) - store1 := snapstate.Store(s.state) - c.Check(store1, Equals, sto) - - // cached - store2 := snapstate.Store(s.state) - c.Check(store2, Equals, sto) } const ( @@ -175,6 +173,7 @@ kinds := taskKinds(ts.Tasks()) expected := []string{ + "prerequisites", "download-snap", "validate-snap", "mount-snap", @@ -221,6 +220,7 @@ kinds := taskKinds(ts.Tasks()) expected := []string{ + "prerequisites", "download-snap", "validate-snap", "mount-snap", @@ -361,6 +361,9 @@ }) defer reset() + // this needs doing because dirs depends on the release info + dirs.SetRootDir(dirs.GlobalRootDir) + _, err := snapstate.Install(s.state, "some-snap", "channel-for-classic", snap.R(0), s.user.ID, snapstate.Flags{Classic: true}) c.Assert(err, ErrorMatches, "classic confinement requires snaps under /snap or symlink from /snap to "+dirs.SnapMountDir) } @@ -423,7 +426,9 @@ c.Check(flag, Equals, true) } -func (s *snapmgrTestSuite) testRevertTasks(flags snapstate.Flags, c *C) { +type fullFlags struct{ before, change, after, setup snapstate.Flags } + +func (s *snapmgrTestSuite) testRevertTasksFullFlags(flags fullFlags, c *C) { s.state.Lock() defer s.state.Unlock() @@ -433,15 +438,18 @@ {RealName: "some-snap", Revision: snap.R(7)}, {RealName: "some-snap", Revision: snap.R(11)}, }, + Flags: flags.before, Current: snap.R(11), SnapType: "app", }) - ts, err := snapstate.Revert(s.state, "some-snap", flags) + ts, err := snapstate.Revert(s.state, "some-snap", flags.change) c.Assert(err, IsNil) - c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) - c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ + tasks := ts.Tasks() + c.Assert(s.state.TaskCount(), Equals, len(tasks)) + c.Assert(taskKinds(tasks), DeepEquals, []string{ + "prerequisites", "prepare-snap", "stop-snap-services", "remove-aliases", @@ -454,24 +462,67 @@ "run-hook[configure]", }) + snapsup, err := snapstate.TaskSnapSetup(tasks[0]) + c.Assert(err, IsNil) + flags.setup.Revert = true + c.Check(snapsup.Flags, Equals, flags.setup) + chg := s.state.NewChange("revert", "revert snap") chg.AddAll(ts) s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() var snapst snapstate.SnapState err = snapstate.Get(s.state, "some-snap", &snapst) c.Assert(err, IsNil) - c.Check(snapst.Flags, Equals, flags) + c.Check(snapst.Flags, Equals, flags.after) +} + +func (s *snapmgrTestSuite) testRevertTasks(flags snapstate.Flags, c *C) { + s.testRevertTasksFullFlags(fullFlags{before: flags, change: flags, after: flags, setup: flags}, c) } func (s *snapmgrTestSuite) TestRevertTasks(c *C) { s.testRevertTasks(snapstate.Flags{}, c) } +func (s *snapmgrTestSuite) TestRevertTasksFromDevMode(c *C) { + // the snap is installed in devmode, but the request to revert does not specify devmode + s.testRevertTasksFullFlags(fullFlags{ + before: snapstate.Flags{DevMode: true}, // the snap is installed in devmode + change: snapstate.Flags{}, // the request to revert does not specify devmode + after: snapstate.Flags{DevMode: true}, // the reverted snap is installed in devmode + setup: snapstate.Flags{DevMode: true}, // because setup said so + }, c) +} + +func (s *snapmgrTestSuite) TestRevertTasksFromJailMode(c *C) { + // the snap is installed in jailmode, but the request to revert does not specify jailmode + s.testRevertTasksFullFlags(fullFlags{ + before: snapstate.Flags{JailMode: true}, // the snap is installed in jailmode + change: snapstate.Flags{}, // the request to revert does not specify jailmode + after: snapstate.Flags{JailMode: true}, // the reverted snap is installed in jailmode + setup: snapstate.Flags{JailMode: true}, // because setup said so + }, c) +} + +func (s *snapmgrTestSuite) TestRevertTasksFromClassic(c *C) { + if !dirs.SupportsClassicConfinement() { + c.Skip("no support for classic") + } + + // the snap is installed in classic, but the request to revert does not specify classic + s.testRevertTasksFullFlags(fullFlags{ + before: snapstate.Flags{Classic: true}, // the snap is installed in classic + change: snapstate.Flags{}, // the request to revert does not specify classic + after: snapstate.Flags{Classic: true}, // the reverted snap is installed in classic + setup: snapstate.Flags{Classic: true}, // because setup said so + }, c) +} + func (s *snapmgrTestSuite) TestRevertTasksDevMode(c *C) { s.testRevertTasks(snapstate.Flags{DevMode: true}, c) } @@ -482,7 +533,7 @@ func (s *snapmgrTestSuite) TestRevertTasksClassic(c *C) { if !dirs.SupportsClassicConfinement() { - return + c.Skip("no support for classic") } s.testRevertTasks(snapstate.Flags{Classic: true}, c) } @@ -579,7 +630,7 @@ func (s *snapmgrTestSuite) TestUpdateManyClassicConfinementFiltering(c *C) { if !dirs.SupportsClassicConfinement() { - return + c.Skip("no support for classic") } s.state.Lock() @@ -601,7 +652,7 @@ func (s *snapmgrTestSuite) TestUpdateManyClassic(c *C) { if !dirs.SupportsClassicConfinement() { - return + c.Skip("no support for classic") } s.state.Lock() @@ -674,11 +725,12 @@ }) validateCalled := false - validateRefreshes := func(st *state.State, refreshes []*snap.Info, userID int) ([]*snap.Info, error) { + validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int) ([]*snap.Info, error) { validateCalled = true c.Check(refreshes, HasLen, 1) c.Check(refreshes[0].SnapID, Equals, "some-snap-id") c.Check(refreshes[0].Revision, Equals, snap.R(11)) + c.Check(ignoreValidation, HasLen, 0) return refreshes, nil } // hook it up @@ -706,10 +758,11 @@ }) validateErr := errors.New("refresh control error") - validateRefreshes := func(st *state.State, refreshes []*snap.Info, userID int) ([]*snap.Info, error) { + validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int) ([]*snap.Info, error) { c.Check(refreshes, HasLen, 1) c.Check(refreshes[0].SnapID, Equals, "some-snap-id") c.Check(refreshes[0].Revision, Equals, snap.R(11)) + c.Check(ignoreValidation, HasLen, 0) return nil, validateErr } // hook it up @@ -751,6 +804,7 @@ // ensure that we do not run any form of garbage-collection c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ + "prerequisites", "prepare-snap", "stop-snap-services", "remove-aliases", @@ -936,6 +990,7 @@ Aliases: map[string]*snapstate.AliasTarget{ "foo.bar": {Manual: "bar"}, }, + SnapType: "app", }) _, err := snapstate.Install(s.state, "foo", "some-channel", snap.R(0), 0, snapstate.Flags{}) @@ -955,17 +1010,17 @@ Channel: "edge", Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}}, Current: snap.R(1), + SnapType: "app", }) s.state.Unlock() return s.fakeStore.SnapInfo(spec, user) } func (s *snapmgrTestSuite) TestInstallStateConflict(c *C) { - s.state.Lock() defer s.state.Unlock() - snapstate.ReplaceStore(s.state, sneakyStore{fakeStore: s.fakeStore, state: s.state}) + storestate.ReplaceStore(s.state, sneakyStore{fakeStore: s.fakeStore, state: s.state}) _, err := snapstate.Install(s.state, "some-snap", "some-channel", snap.R(0), 0, snapstate.Flags{}) c.Assert(err, ErrorMatches, `snap "some-snap" state changed during install preparations`) @@ -1031,7 +1086,7 @@ }) validateCalled := false - happyValidateRefreshes := func(st *state.State, refreshes []*snap.Info, userID int) ([]*snap.Info, error) { + happyValidateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int) ([]*snap.Info, error) { validateCalled = true return refreshes, nil } @@ -1082,7 +1137,7 @@ func (s *snapmgrTestSuite) TestUpdateDevModeConfinementFiltering(c *C) { if !dirs.SupportsClassicConfinement() { - return + c.Skip("no support for classic") } s.state.Lock() @@ -1108,7 +1163,7 @@ func (s *snapmgrTestSuite) TestUpdateClassicConfinementFiltering(c *C) { if !dirs.SupportsClassicConfinement() { - return + c.Skip("no support for classic") } s.state.Lock() @@ -1136,7 +1191,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // verify snap is in classic @@ -1148,7 +1203,7 @@ func (s *snapmgrTestSuite) TestUpdateClassicFromClassic(c *C) { if !dirs.SupportsClassicConfinement() { - return + c.Skip("no support for classic") } s.state.Lock() @@ -1208,7 +1263,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // verify snap is in classic @@ -1220,7 +1275,7 @@ func (s *snapmgrTestSuite) TestUpdateStrictFromClassic(c *C) { if !dirs.SupportsClassicConfinement() { - return + c.Skip("no support for classic") } s.state.Lock() @@ -1394,7 +1449,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // ensure all our tasks ran @@ -1477,7 +1532,7 @@ // check progress ta := ts.Tasks() - task := ta[0] + task := ta[1] _, cur, total := task.Progress() c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress) c.Assert(total, Equals, s.fakeStore.fakeTotalProgress) @@ -1565,7 +1620,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -1575,7 +1630,6 @@ Channel: "some-channel", SnapID: "services-snap-id", Revision: snap.R(7), - Epoch: "0", }, revno: snap.R(11), }, @@ -1666,7 +1720,7 @@ c.Assert(s.fakeBackend.ops, DeepEquals, expected) // check progress - task := ts.Tasks()[0] + task := ts.Tasks()[1] _, cur, total := task.Progress() c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress) c.Assert(total, Equals, s.fakeStore.fakeTotalProgress) @@ -1693,7 +1747,7 @@ }) // check post-refresh hook - task = ts.Tasks()[11] + task = ts.Tasks()[12] c.Assert(task.Kind(), Equals, "run-hook") c.Assert(task.Summary(), Matches, `Run post-refresh hook of "services-snap" snap if present`) @@ -1744,7 +1798,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -1754,7 +1808,6 @@ Channel: "some-channel", SnapID: "some-snap-id", Revision: snap.R(7), - Epoch: "", }, revno: snap.R(11), }, @@ -1904,7 +1957,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -1914,7 +1967,6 @@ Channel: "some-channel", SnapID: "some-snap-id", Revision: snap.R(7), - Epoch: "", }, revno: snap.R(11), }, @@ -2107,7 +2159,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -2120,7 +2172,6 @@ Channel: "channel-for-7", SnapID: "some-snap-id", Revision: snap.R(7), - Epoch: "", }, }, } @@ -2177,10 +2228,11 @@ }) validateErr := errors.New("refresh control error") - validateRefreshes := func(st *state.State, refreshes []*snap.Info, userID int) ([]*snap.Info, error) { + validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int) ([]*snap.Info, error) { c.Check(refreshes, HasLen, 1) c.Check(refreshes[0].SnapID, Equals, "some-snap-id") c.Check(refreshes[0].Revision, Equals, snap.R(11)) + c.Check(ignoreValidation, HasLen, 0) return nil, validateErr } // hook it up @@ -2208,7 +2260,7 @@ }) validateErr := errors.New("refresh control error") - validateRefreshes := func(st *state.State, refreshes []*snap.Info, userID int) ([]*snap.Info, error) { + validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int) ([]*snap.Info, error) { return nil, validateErr } // hook it up @@ -2224,6 +2276,140 @@ c.Check(snapsup.Flags, DeepEquals, flags.ForSnapSetup()) } +func (s *snapmgrTestSuite) TestUpdateIgnoreValidationSticky(c *C) { + si := snap.SideInfo{ + RealName: "some-snap", + SnapID: "some-snap-id", + Revision: snap.R(7), + } + + s.state.Lock() + defer s.state.Unlock() + + snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ + Active: true, + Sequence: []*snap.SideInfo{&si}, + Current: si.Revision, + SnapType: "app", + }) + + validateErr := errors.New("refresh control error") + validateRefreshesFail := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int) ([]*snap.Info, error) { + c.Check(refreshes, HasLen, 1) + if len(ignoreValidation) == 0 { + return nil, validateErr + } + c.Check(ignoreValidation, DeepEquals, map[string]bool{ + "some-snap-id": true, + }) + return refreshes, nil + } + // hook it up + snapstate.ValidateRefreshes = validateRefreshesFail + + flags := snapstate.Flags{IgnoreValidation: true} + ts, err := snapstate.Update(s.state, "some-snap", "stable", snap.R(0), s.user.ID, flags) + c.Assert(err, IsNil) + + c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{ + op: "storesvc-list-refresh", + revno: snap.R(11), + cand: store.RefreshCandidate{ + SnapID: "some-snap-id", + Revision: snap.R(7), + Channel: "stable", + IgnoreValidation: true, + }, + }) + + chg := s.state.NewChange("refresh", "refresh snap") + chg.AddAll(ts) + + s.state.Unlock() + defer s.snapmgr.Stop() + s.settle(c) + s.state.Lock() + + // verify snap has IgnoreValidation set + var snapst snapstate.SnapState + err = snapstate.Get(s.state, "some-snap", &snapst) + c.Assert(err, IsNil) + c.Check(snapst.IgnoreValidation, Equals, true) + c.Check(snapst.Current, Equals, snap.R(11)) + + s.fakeBackend.ops = nil + s.fakeStore.refreshRevnos = map[string]snap.Revision{ + "some-snap-id": snap.R(12), + } + _, tts, err := snapstate.UpdateMany(s.state, []string{"some-snap"}, s.user.ID) + c.Assert(err, IsNil) + c.Check(tts, HasLen, 1) + + c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{ + op: "storesvc-list-refresh", + revno: snap.R(12), + cand: store.RefreshCandidate{ + SnapID: "some-snap-id", + Revision: snap.R(11), + Channel: "stable", + IgnoreValidation: true, + }, + }) + + chg = s.state.NewChange("refresh", "refresh snaps") + chg.AddAll(tts[0]) + + s.state.Unlock() + defer s.snapmgr.Stop() + s.settle(c) + s.state.Lock() + + snapst = snapstate.SnapState{} + err = snapstate.Get(s.state, "some-snap", &snapst) + c.Assert(err, IsNil) + c.Check(snapst.IgnoreValidation, Equals, true) + c.Check(snapst.Current, Equals, snap.R(12)) + + // reset ignore validation + s.fakeBackend.ops = nil + s.fakeStore.refreshRevnos = map[string]snap.Revision{ + "some-snap-id": snap.R(11), + } + validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int) ([]*snap.Info, error) { + return refreshes, nil + } + // hook it up + snapstate.ValidateRefreshes = validateRefreshes + flags = snapstate.Flags{} + ts, err = snapstate.Update(s.state, "some-snap", "stable", snap.R(0), s.user.ID, flags) + c.Assert(err, IsNil) + + c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{ + op: "storesvc-list-refresh", + revno: snap.R(11), + cand: store.RefreshCandidate{ + SnapID: "some-snap-id", + Revision: snap.R(12), + Channel: "stable", + IgnoreValidation: false, + }, + }) + + chg = s.state.NewChange("refresh", "refresh snap") + chg.AddAll(ts) + + s.state.Unlock() + defer s.snapmgr.Stop() + s.settle(c) + s.state.Lock() + + snapst = snapstate.SnapState{} + err = snapstate.Get(s.state, "some-snap", &snapst) + c.Assert(err, IsNil) + c.Check(snapst.IgnoreValidation, Equals, false) + c.Check(snapst.Current, Equals, snap.R(11)) +} + func (s *snapmgrTestSuite) TestSingleUpdateBlockedRevision(c *C) { // single updates should *not* set the block list si7 := snap.SideInfo{ @@ -2244,6 +2430,7 @@ Active: true, Sequence: []*snap.SideInfo{&si7, &si11}, Current: si7.Revision, + SnapType: "app", }) _, err := snapstate.Update(s.state, "some-snap", "some-channel", snap.R(0), s.user.ID, snapstate.Flags{}) @@ -2256,7 +2443,6 @@ cand: store.RefreshCandidate{ SnapID: "some-snap-id", Revision: snap.R(7), - Epoch: "", Channel: "some-channel", }, }) @@ -2283,6 +2469,7 @@ Active: true, Sequence: []*snap.SideInfo{&si7, &si11}, Current: si7.Revision, + SnapType: "app", }) updates, _, err := snapstate.UpdateMany(s.state, []string{"some-snap"}, s.user.ID) @@ -2462,7 +2649,7 @@ j++ expectedUpdatesSet["some-snap"] = true first := updateTs.Tasks()[0] - c.Check(first.Kind(), Equals, "download-snap") + c.Check(first.Kind(), Equals, "prerequisites") wait := false if expectedPruned["other-snap"]["aliasA"] { wait = true @@ -2603,8 +2790,8 @@ } if scenario.update { first := tasks[j] - j += 15 - c.Check(first.Kind(), Equals, "download-snap") + j += 16 + c.Check(first.Kind(), Equals, "prerequisites") wait := false if expectedPruned["other-snap"]["aliasA"] { wait = true @@ -2703,7 +2890,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -2759,7 +2946,7 @@ // verify snapSetup info var snapsup snapstate.SnapSetup - task := ts.Tasks()[0] + task := ts.Tasks()[1] err = task.Get("snap-setup", &snapsup) c.Assert(err, IsNil) c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ @@ -2797,7 +2984,8 @@ Sequence: []*snap.SideInfo{ {RealName: "mock", Revision: snap.R(-2)}, }, - Current: snap.R(-2), + Current: snap.R(-2), + SnapType: "app", }) mockSnap := makeTestSnap(c, `name: mock @@ -2809,7 +2997,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() ops := s.fakeBackend.ops @@ -2850,7 +3038,7 @@ // verify snapSetup info var snapsup snapstate.SnapSetup - task := ts.Tasks()[0] + task := ts.Tasks()[1] err = task.Get("snap-setup", &snapsup) c.Assert(err, IsNil) c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ @@ -2889,7 +3077,8 @@ Sequence: []*snap.SideInfo{ {RealName: "mock", Revision: snap.R(100001)}, }, - Current: snap.R(100001), + Current: snap.R(100001), + SnapType: "app", }) mockSnap := makeTestSnap(c, `name: mock @@ -2901,7 +3090,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -2999,7 +3188,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // ensure only local install was run, i.e. first actions are pseudo-action current @@ -3065,7 +3254,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -3175,7 +3364,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -3309,7 +3498,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Check(len(s.fakeBackend.ops), Equals, 2) @@ -3374,7 +3563,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Check(len(s.fakeBackend.ops), Equals, 5) @@ -3581,7 +3770,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // verify snaps in the system state @@ -3634,7 +3823,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // verify snaps in the system state @@ -3675,7 +3864,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() cfgs = nil @@ -3722,7 +3911,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // config snapshot of rev. 2 has been made by 'revert' @@ -3763,7 +3952,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // ensure garbage collection runs as the last tasks @@ -3925,7 +4114,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -4009,7 +4198,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Assert(s.fakeBackend.ops.Ops(), HasLen, 6) @@ -4053,7 +4242,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -4134,7 +4323,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -4230,7 +4419,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -4347,7 +4536,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -4409,7 +4598,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -4463,7 +4652,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // switch is not really really doing anything backend related @@ -4514,7 +4703,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ @@ -4607,7 +4796,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // verify we generated a failure report @@ -4619,8 +4808,9 @@ "Revision": "11", }) c.Check(errMsg, Matches, `(?sm)change "install": "install a snap" -download-snap: Undoing +prerequisites: Done snap-setup: "some-snap" \(11\) "some-channel" +download-snap: Undoing validate-snap: Done .* link-snap: Error @@ -4633,8 +4823,9 @@ cleanup: Hold run-hook: Hold`) c.Check(errSig, Matches, `(?sm)snap-install: -download-snap: Undoing +prerequisites: Done snap-setup: "some-snap" +download-snap: Undoing validate-snap: Done .* link-snap: Error @@ -4655,7 +4846,7 @@ chg.AddAll(ts) s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // verify that we excluded this field from the bugreport c.Check(n, Equals, 2) @@ -4687,11 +4878,8 @@ defer s.state.Unlock() snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } - logbuf := bytes.NewBuffer(nil) - l, err := logger.New(logbuf, logger.DefaultFlags) - c.Assert(err, IsNil) - logger.SetLogger(l) - defer logger.SetLogger(logger.NullLogger) + logbuf, restore := logger.MockLogger() + defer restore() s.state.Set("last-refresh", time.Date(2009, 8, 13, 8, 0, 5, 0, time.UTC)) tr := config.NewTransaction(s.state) @@ -4861,7 +5049,7 @@ // run the changes s.state.Unlock() - s.settle() + s.settle(c) s.state.Lock() s.verifyRefreshLast(c) @@ -4936,7 +5124,8 @@ } type snapmgrQuerySuite struct { - st *state.State + st *state.State + restore func() } var _ = Suite(&snapmgrQuerySuite{}) @@ -4946,6 +5135,11 @@ st.Lock() defer st.Unlock() + restoreSanitize := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) + s.restore = func() { + restoreSanitize() + } + s.st = st dirs.SetRootDir(c.MkDir()) @@ -4980,6 +5174,7 @@ func (s *snapmgrQuerySuite) TearDownTest(c *C) { dirs.SetRootDir("") + s.restore() } func (s *snapmgrQuerySuite) TestInfo(c *C) { @@ -5276,7 +5471,7 @@ } func (s *snapmgrTestSuite) TestTrySetsTryModeClassic(c *C) { if !dirs.SupportsClassicConfinement() { - return + c.Skip("no support for classic") } s.testTrySetsTryMode(snapstate.Flags{Classic: true}, c) } @@ -5299,7 +5494,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // verify snap is in TryMode @@ -5312,6 +5507,7 @@ c.Check(s.state.TaskCount(), Equals, len(ts.Tasks())) c.Check(taskKinds(ts.Tasks()), DeepEquals, []string{ + "prerequisites", "prepare-snap", "mount-snap", "copy-snap-data", @@ -5329,7 +5525,7 @@ func (s *snapmgrTestSuite) TestTryUndoRemovesTryFlag(c *C) { if !dirs.SupportsClassicConfinement() { - return + c.Skip("no support for classic") } s.testTrySetsTryMode(snapstate.Flags{}, c) } @@ -5342,7 +5538,7 @@ } func (s *snapmgrTestSuite) TestTryUndoRemovesTryFlagLeavesClassic(c *C) { if !dirs.SupportsClassicConfinement() { - return + c.Skip("no support for classic") } s.testTrySetsTryMode(snapstate.Flags{Classic: true}, c) } @@ -5376,7 +5572,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // verify snap is not in try mode, the state got undone @@ -5584,7 +5780,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() var snapst snapstate.SnapState @@ -5888,7 +6084,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() expected := fakeOps{ { @@ -6135,10 +6331,23 @@ c.Assert(hookstate.HookTask(s.state, "", hooksup, contextData), NotNil) } +func makeInstalledMockCoreSnap(c *C) { + coreSnapYaml := `name: core +version: 1.0 +type: os +` + snaptest.MockSnap(c, coreSnapYaml, "", &snap.SideInfo{ + RealName: "core", + Revision: snap.R(1), + }) +} + func (s *snapmgrTestSuite) TestGadgetDefaults(c *C) { r := release.MockOnClassic(false) defer r() + makeInstalledMockCoreSnap(c) + // using MockSnap, we want to read the bits on disk snapstate.MockReadInfo(snap.ReadInfo) @@ -6167,6 +6376,8 @@ r := release.MockOnClassic(false) defer r() + makeInstalledMockCoreSnap(c) + // using MockSnap, we want to read the bits on disk snapstate.MockReadInfo(snap.ReadInfo) @@ -6188,6 +6399,8 @@ } func (s *snapmgrTestSuite) TestGadgetDefaultsInstalled(c *C) { + makeInstalledMockCoreSnap(c) + // using MockSnap, we want to read the bits on disk snapstate.MockReadInfo(snap.ReadInfo) @@ -6251,6 +6464,7 @@ s.state.Lock() defer s.state.Unlock() + snapstate.Set(s.state, "core", nil) snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ Active: true, Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}}, @@ -6301,6 +6515,7 @@ s.state.Lock() defer s.state.Unlock() + snapstate.Set(s.state, "core", nil) snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ Active: true, Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}}, @@ -6317,7 +6532,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // ensure all our tasks ran @@ -6465,7 +6680,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() // ensure all our tasks ran @@ -6536,7 +6751,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Check(s.state.Changes(), HasLen, 1) @@ -6559,7 +6774,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Check(s.state.Changes(), HasLen, 0) @@ -6569,7 +6784,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Check(s.state.Changes(), HasLen, 1) @@ -6593,7 +6808,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() c.Check(s.state.Changes(), HasLen, 1) @@ -6657,7 +6872,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() var snapst2 snapstate.SnapState @@ -6676,7 +6891,7 @@ c.Assert(release.ReleaseInfo.ForceDevMode(), Equals, true) defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() defer s.state.Unlock() @@ -6711,7 +6926,7 @@ s.state.Unlock() defer s.snapmgr.Stop() - s.settle() + s.settle(c) s.state.Lock() var snapst2 snapstate.SnapState @@ -6741,6 +6956,7 @@ return nil, nil } + snapstate.Set(s.state, "core", nil) snapstate.Set(s.state, "alias-snap", &snapstate.SnapState{ Sequence: []*snap.SideInfo{ {RealName: "alias-snap", Revision: snap.R(11)}, @@ -6808,6 +7024,7 @@ return nil, nil } + snapstate.Set(s.state, "core", nil) snapstate.Set(s.state, "alias-snap", &snapstate.SnapState{ Sequence: []*snap.SideInfo{ {RealName: "alias-snap", Revision: snap.R(11)}, @@ -6918,6 +7135,446 @@ } } +func (s *snapmgrTestSuite) TestInstallWithoutCoreRunThrough1(c *C) { + s.state.Lock() + defer s.state.Unlock() + + // pretend we don't have core + snapstate.Set(s.state, "core", nil) + + chg := s.state.NewChange("install", "install a snap on a system without core") + ts, err := snapstate.Install(s.state, "some-snap", "some-channel", snap.R(42), s.user.ID, snapstate.Flags{}) + c.Assert(err, IsNil) + chg.AddAll(ts) + + s.state.Unlock() + defer s.snapmgr.Stop() + s.settle(c) + s.state.Lock() + + // ensure all our tasks ran + c.Assert(chg.Err(), IsNil) + c.Assert(chg.IsReady(), Equals, true) + c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{ + { + macaroon: s.user.StoreMacaroon, + name: "core", + }, + { + macaroon: s.user.StoreMacaroon, + name: "some-snap", + }}) + expected := fakeOps{ + // we check the snap + { + op: "storesvc-snap", + name: "some-snap", + revno: snap.R(42), + }, + // then we check core because its not installed already + // and continue with that + { + op: "storesvc-snap", + name: "core", + revno: snap.R(11), + }, + { + op: "storesvc-download", + name: "core", + }, + { + op: "validate-snap:Doing", + name: "core", + revno: snap.R(11), + }, + { + op: "current", + old: "", + }, + { + op: "open-snap-file", + name: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), + sinfo: snap.SideInfo{ + RealName: "core", + Channel: "stable", + SnapID: "snapIDsnapidsnapidsnapidsnapidsn", + Revision: snap.R(11), + }, + }, + { + op: "setup-snap", + name: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), + revno: snap.R(11), + }, + { + op: "copy-data", + name: filepath.Join(dirs.SnapMountDir, "core/11"), + old: "", + }, + { + op: "setup-profiles:Doing", + name: "core", + revno: snap.R(11), + }, + { + op: "candidate", + sinfo: snap.SideInfo{ + RealName: "core", + Channel: "stable", + SnapID: "snapIDsnapidsnapidsnapidsnapidsn", + Revision: snap.R(11), + }, + }, + { + op: "link-snap", + name: filepath.Join(dirs.SnapMountDir, "core/11"), + }, + { + op: "update-aliases", + }, + // after core is in place continue with the snap + { + op: "storesvc-download", + name: "some-snap", + }, + { + op: "validate-snap:Doing", + name: "some-snap", + revno: snap.R(42), + }, + { + op: "current", + old: "", + }, + { + op: "open-snap-file", + name: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), + sinfo: snap.SideInfo{ + RealName: "some-snap", + Channel: "some-channel", + SnapID: "snapIDsnapidsnapidsnapidsnapidsn", + Revision: snap.R(42), + }, + }, + { + op: "setup-snap", + name: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), + revno: snap.R(42), + }, + { + op: "copy-data", + name: filepath.Join(dirs.SnapMountDir, "some-snap/42"), + old: "", + }, + { + op: "setup-profiles:Doing", + name: "some-snap", + revno: snap.R(42), + }, + { + op: "candidate", + sinfo: snap.SideInfo{ + RealName: "some-snap", + Channel: "some-channel", + SnapID: "snapIDsnapidsnapidsnapidsnapidsn", + Revision: snap.R(42), + }, + }, + { + op: "link-snap", + name: filepath.Join(dirs.SnapMountDir, "some-snap/42"), + }, + { + op: "update-aliases", + }, + // cleanups order is random + { + op: "cleanup-trash", + name: "core", + revno: snap.R(11), + }, + { + op: "cleanup-trash", + name: "some-snap", + revno: snap.R(42), + }, + } + // start with an easier-to-read error if this fails: + c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) + // compare the details without the cleanup tasks, the order is random + // as they run in parallel + opsLenWithoutCleanups := len(s.fakeBackend.ops) - 2 + c.Assert(s.fakeBackend.ops[:opsLenWithoutCleanups], DeepEquals, expected[:opsLenWithoutCleanups]) + + // verify core in the system state + var snaps map[string]*snapstate.SnapState + err = s.state.Get("snaps", &snaps) + c.Assert(err, IsNil) + + snapst := snaps["core"] + c.Assert(snapst.Active, Equals, true) + c.Assert(snapst.Channel, Equals, "stable") + c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ + RealName: "core", + Channel: "stable", + SnapID: "snapIDsnapidsnapidsnapidsnapidsn", + Revision: snap.R(11), + }) +} + +func (s *snapmgrTestSuite) TestInstallWithoutCoreTwoSnapsRunThrough(c *C) { + s.state.Lock() + defer s.state.Unlock() + + restore := snapstate.MockPrerequisitesRetryTimeout(10 * time.Millisecond) + defer restore() + + // pretend we don't have core + snapstate.Set(s.state, "core", nil) + + chg1 := s.state.NewChange("install", "install snap 1") + ts1, err := snapstate.Install(s.state, "snap1", "some-channel", snap.R(42), s.user.ID, snapstate.Flags{}) + c.Assert(err, IsNil) + chg1.AddAll(ts1) + + chg2 := s.state.NewChange("install", "install snap 2") + ts2, err := snapstate.Install(s.state, "snap2", "some-other-channel", snap.R(21), s.user.ID, snapstate.Flags{}) + c.Assert(err, IsNil) + chg2.AddAll(ts2) + + s.state.Unlock() + defer s.snapmgr.Stop() + s.settle(c) + s.state.Lock() + + // ensure all our tasks ran and core was only installed once + c.Assert(chg1.Err(), IsNil) + c.Assert(chg2.Err(), IsNil) + + c.Assert(chg1.IsReady(), Equals, true) + c.Assert(chg2.IsReady(), Equals, true) + + // order in which the changes run is random + len1 := len(chg1.Tasks()) + len2 := len(chg2.Tasks()) + if len1 > len2 { + c.Assert(chg1.Tasks(), HasLen, 24) + c.Assert(chg2.Tasks(), HasLen, 12) + } else { + c.Assert(chg1.Tasks(), HasLen, 12) + c.Assert(chg2.Tasks(), HasLen, 24) + } + + // FIXME: add helpers and do a DeepEquals here for the operations +} + +func (s *snapmgrTestSuite) TestInstallWithoutCoreTwoSnapsWithFailureRunThrough(c *C) { + s.state.Lock() + defer s.state.Unlock() + + // slightly longer retry timeout to avoid deadlock when we + // trigger a retry quickly that the link snap for core does + // not have a chance to run + restore := snapstate.MockPrerequisitesRetryTimeout(40 * time.Millisecond) + defer restore() + + defer s.snapmgr.Stop() + // Two changes are created, the first will fails, the second will + // be fine. The order of what change runs first is random, the + // first change will also install core in its own lane. This test + // ensures that core gets installed and there are no conflicts + // even if core already got installed from the first change. + // + // It runs multiple times so that both possible cases get a chance + // to run + for i := 0; i < 5; i++ { + // start clean + snapstate.Set(s.state, "core", nil) + snapstate.Set(s.state, "snap2", nil) + + // chg1 has an error + chg1 := s.state.NewChange("install", "install snap 1") + ts1, err := snapstate.Install(s.state, "snap1", "some-channel", snap.R(42), s.user.ID, snapstate.Flags{}) + c.Assert(err, IsNil) + chg1.AddAll(ts1) + + tasks := ts1.Tasks() + last := tasks[len(tasks)-1] + terr := s.state.NewTask("error-trigger", "provoking total undo") + terr.WaitFor(last) + chg1.AddTask(terr) + + // chg2 is good + chg2 := s.state.NewChange("install", "install snap 2") + ts2, err := snapstate.Install(s.state, "snap2", "some-other-channel", snap.R(21), s.user.ID, snapstate.Flags{}) + c.Assert(err, IsNil) + chg2.AddAll(ts2) + + // we use our own settle as we need a bigger timeout + s.state.Unlock() + err = s.o.Settle(15 * time.Second) + s.state.Lock() + c.Assert(err, IsNil) + + // ensure expected change states + c.Check(chg1.Status(), Equals, state.ErrorStatus) + c.Check(chg2.Status(), Equals, state.DoneStatus) + + // ensure we have both core and snap2 + var snapst snapstate.SnapState + + err = snapstate.Get(s.state, "core", &snapst) + c.Assert(err, IsNil) + c.Assert(snapst.Active, Equals, true) + c.Assert(snapst.Sequence, HasLen, 1) + c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ + RealName: "core", + SnapID: "snapIDsnapidsnapidsnapidsnapidsn", + Channel: "stable", + Revision: snap.R(11), + }) + + err = snapstate.Get(s.state, "snap2", &snapst) + c.Assert(err, IsNil) + c.Assert(snapst.Active, Equals, true) + c.Assert(snapst.Sequence, HasLen, 1) + c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ + RealName: "snap2", + SnapID: "snapIDsnapidsnapidsnapidsnapidsn", + Channel: "some-other-channel", + Revision: snap.R(21), + }) + + } +} + +type behindYourBackStore struct { + *fakeStore + state *state.State + + coreInstallRequested bool + coreInstalled bool + chg *state.Change +} + +func (s behindYourBackStore) SnapInfo(spec store.SnapSpec, user *auth.UserState) (*snap.Info, error) { + if spec.Name == "core" { + s.state.Lock() + if !s.coreInstallRequested { + s.coreInstallRequested = true + snapsup := &snapstate.SnapSetup{ + SideInfo: &snap.SideInfo{ + RealName: "core", + }, + } + t := s.state.NewTask("prepare", "prepare core") + t.Set("snap-setup", snapsup) + s.chg = s.state.NewChange("install", "install core") + s.chg.AddAll(state.NewTaskSet(t)) + } + if s.chg != nil && !s.coreInstalled { + // marks change ready but also + // tasks need to also be marked cleaned + for _, t := range s.chg.Tasks() { + t.SetStatus(state.DoneStatus) + t.SetClean() + } + snapstate.Set(s.state, "core", &snapstate.SnapState{ + Active: true, + Sequence: []*snap.SideInfo{ + {RealName: "core", Revision: snap.R(1)}, + }, + Current: snap.R(1), + SnapType: "os", + }) + s.coreInstalled = true + } + s.state.Unlock() + } + + return s.fakeStore.SnapInfo(spec, user) +} + +// this test the scenario that some-snap gets installed and during the +// install (when unlocking for the store info call for core) an +// explicit "snap install core" happens. In this case the snapstate +// will return a change conflict. we handle this via a retry, ensure +// this is actually what happens. +func (s *snapmgrTestSuite) TestInstallWithoutCoreConflictingInstall(c *C) { + s.state.Lock() + defer s.state.Unlock() + + restore := snapstate.MockPrerequisitesRetryTimeout(10 * time.Millisecond) + defer restore() + + storestate.ReplaceStore(s.state, behindYourBackStore{fakeStore: s.fakeStore, state: s.state}) + + // pretend we don't have core + snapstate.Set(s.state, "core", nil) + + // now install a snap that will pull in core + chg := s.state.NewChange("install", "install a snap on a system without core") + ts, err := snapstate.Install(s.state, "some-snap", "some-channel", snap.R(42), s.user.ID, snapstate.Flags{}) + c.Assert(err, IsNil) + chg.AddAll(ts) + + prereq := ts.Tasks()[0] + c.Assert(prereq.Kind(), Equals, "prerequisites") + c.Check(prereq.AtTime().IsZero(), Equals, true) + + s.state.Unlock() + defer s.snapmgr.Stop() + + // start running the change, this will trigger the + // prerequisites task, which will trigger the install of core + // and also call our mock store which will generate a parallel + // change + s.snapmgr.Ensure() + s.snapmgr.Wait() + + // change is not ready yet, because the prerequists triggered + // a state.Retry{} because of the conflicting change + c.Assert(chg.IsReady(), Equals, false) + s.state.Lock() + // marked for retry + c.Check(prereq.AtTime().IsZero(), Equals, false) + c.Check(prereq.Status().Ready(), Equals, false) + s.state.Unlock() + + // retry interval is 10ms so 20ms should be plenty of time + time.Sleep(20 * time.Millisecond) + s.settle(c) + // chg got retried, core is now installed, things are good + c.Assert(chg.IsReady(), Equals, true) + + s.state.Lock() + + // ensure all our tasks ran + c.Assert(chg.Err(), IsNil) + c.Assert(chg.IsReady(), Equals, true) + + // verify core in the system state + var snaps map[string]*snapstate.SnapState + err = s.state.Get("snaps", &snaps) + c.Assert(err, IsNil) + + snapst := snaps["core"] + c.Assert(snapst.Active, Equals, true) + c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ + RealName: "core", + Revision: snap.R(1), + }) + + snapst = snaps["some-snap"] + c.Assert(snapst.Active, Equals, true) + c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ + RealName: "some-snap", + SnapID: "snapIDsnapidsnapidsnapidsnapidsn", + Channel: "some-channel", + Revision: snap.R(42), + }) +} + type canDisableSuite struct{} var _ = Suite(&canDisableSuite{}) diff -Nru snapd-2.28.5/overlord/snapstate/storehelpers.go snapd-2.29.3/overlord/snapstate/storehelpers.go --- snapd-2.28.5/overlord/snapstate/storehelpers.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/overlord/snapstate/storehelpers.go 2017-11-02 15:44:20.000000000 +0000 @@ -22,6 +22,7 @@ import ( "github.com/snapcore/snapd/overlord/auth" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/store" ) @@ -33,7 +34,7 @@ return auth.User(st, userID) } -func updateInfo(st *state.State, snapst *SnapState, channel string, userID int) (*snap.Info, error) { +func updateInfo(st *state.State, snapst *SnapState, channel string, ignoreValidation bool, userID int) (*snap.Info, error) { user, err := userFromUserID(st, userID) if err != nil { return nil, err @@ -49,13 +50,14 @@ refreshCand := &store.RefreshCandidate{ // the desired channel - Channel: channel, - SnapID: curInfo.SnapID, - Revision: curInfo.Revision, - Epoch: curInfo.Epoch, + Channel: channel, + SnapID: curInfo.SnapID, + Revision: curInfo.Revision, + Epoch: curInfo.Epoch, + IgnoreValidation: ignoreValidation, } - theStore := Store(st) + theStore := storestate.Store(st) st.Unlock() // calls to the store should be done without holding the state lock res, err := theStore.LookupRefresh(refreshCand, user) st.Lock() @@ -67,7 +69,7 @@ if err != nil { return nil, err } - theStore := Store(st) + theStore := storestate.Store(st) st.Unlock() // calls to the store should be done without holding the state lock spec := store.SnapSpec{ Name: name, diff -Nru snapd-2.28.5/overlord/storestate/export_test.go snapd-2.29.3/overlord/storestate/export_test.go --- snapd-2.28.5/overlord/storestate/export_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/overlord/storestate/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,35 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package storestate + +import ( + "github.com/snapcore/snapd/overlord/auth" + "github.com/snapcore/snapd/store" +) + +// MockStoreNew mocks store.New as called by storestate.SetupStore. +func MockStoreNew(new func(*store.Config, auth.AuthContext) *store.Store) func() { + storeNew = new + return func() { + storeNew = store.New + } +} + +var CachedAuthContext = cachedAuthContext diff -Nru snapd-2.28.5/overlord/storestate/storestate.go snapd-2.29.3/overlord/storestate/storestate.go --- snapd-2.28.5/overlord/storestate/storestate.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/overlord/storestate/storestate.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,167 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package storestate + +import ( + "fmt" + "io" + "net/url" + + "golang.org/x/net/context" + + "github.com/snapcore/snapd/asserts" + "github.com/snapcore/snapd/overlord/auth" + "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/progress" + "github.com/snapcore/snapd/snap" + "github.com/snapcore/snapd/store" +) + +var storeNew = store.New + +// StoreState holds the state for the store in the system. +type StoreState struct { + // BaseURL is the store API's base URL. + BaseURL string `json:"base-url"` +} + +// BaseURL returns the store API's explicit base URL. +func BaseURL(st *state.State) string { + var storeState StoreState + + err := st.Get("store", &storeState) + if err != nil { + return "" + } + + return storeState.BaseURL +} + +// updateBaseURL updates the store API's base URL in persistent state. +func updateBaseURL(st *state.State, baseURL string) { + var storeState StoreState + st.Get("store", &storeState) + storeState.BaseURL = baseURL + st.Set("store", &storeState) +} + +// A StoreService can find, list available updates and download snaps. +type StoreService interface { + SnapInfo(spec store.SnapSpec, user *auth.UserState) (*snap.Info, error) + Find(search *store.Search, user *auth.UserState) ([]*snap.Info, error) + LookupRefresh(*store.RefreshCandidate, *auth.UserState) (*snap.Info, error) + ListRefresh([]*store.RefreshCandidate, *auth.UserState) ([]*snap.Info, error) + Sections(user *auth.UserState) ([]string, error) + WriteCatalogs(names io.Writer) error + Download(context.Context, string, string, *snap.DownloadInfo, progress.Meter, *auth.UserState) error + + Assertion(assertType *asserts.AssertionType, primaryKey []string, user *auth.UserState) (asserts.Assertion, error) + + SuggestedCurrency() string + Buy(options *store.BuyOptions, user *auth.UserState) (*store.BuyResult, error) + ReadyToBuy(*auth.UserState) error +} + +// SetupStore configures the system's initial store. +func SetupStore(st *state.State, authContext auth.AuthContext) error { + storeConfig, err := initialStoreConfig(st) + if err != nil { + return err + } + sto := storeNew(storeConfig, authContext) + saveAuthContext(st, authContext) + ReplaceStore(st, sto) + return nil +} + +// SetBaseURL reconfigures the base URL of the store API used by the system. +// If the URL is nil the store is reverted to the system's default. +func SetBaseURL(state *state.State, u *url.URL) error { + baseURL := "" + config := store.DefaultConfig() + if u != nil { + baseURL = u.String() + err := config.SetBaseURL(u) + if err != nil { + return err + } + } + store := store.New(config, cachedAuthContext(state)) + ReplaceStore(state, store) + updateBaseURL(state, baseURL) + return nil +} + +func initialStoreConfig(st *state.State) (*store.Config, error) { + config := store.DefaultConfig() + if baseURL := BaseURL(st); baseURL != "" { + u, err := url.Parse(baseURL) + if err != nil { + return nil, fmt.Errorf("invalid store API base URL: %s", err) + } + err = config.SetBaseURL(u) + if err != nil { + return nil, err + } + } + // cache downloads by default + config.CacheDownloads = 5 + return config, nil +} + +type cachedAuthContextKey struct{} + +func saveAuthContext(state *state.State, authContext auth.AuthContext) { + state.Cache(cachedAuthContextKey{}, authContext) +} + +func cachedAuthContext(state *state.State) auth.AuthContext { + cached := state.Cached(cachedAuthContextKey{}) + if cached != nil { + return cached.(auth.AuthContext) + } + panic("internal error: needing the auth context before managers have initialized it") +} + +type cachedStoreKey struct{} + +// ReplaceStore replaces the store used by the system. +func ReplaceStore(state *state.State, store StoreService) { + state.Cache(cachedStoreKey{}, store) +} + +func cachedStore(st *state.State) StoreService { + ubuntuStore := st.Cached(cachedStoreKey{}) + if ubuntuStore == nil { + return nil + } + return ubuntuStore.(StoreService) +} + +// the store implementation has the interface consumed here +var _ StoreService = (*store.Store)(nil) + +// Store returns the store service used by the system. +func Store(st *state.State) StoreService { + if cachedStore := cachedStore(st); cachedStore != nil { + return cachedStore + } + panic("internal error: needing the store before managers have initialized it") +} diff -Nru snapd-2.28.5/overlord/storestate/storestate_test.go snapd-2.29.3/overlord/storestate/storestate_test.go --- snapd-2.28.5/overlord/storestate/storestate_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/overlord/storestate/storestate_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,259 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package storestate_test + +import ( + "io/ioutil" + "net/url" + "os" + "path/filepath" + "testing" + + "github.com/snapcore/snapd/overlord/auth" + "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/overlord/storestate" + "github.com/snapcore/snapd/store" + + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { TestingT(t) } + +type fakeAuthContext struct{} + +func (*fakeAuthContext) Device() (*auth.DeviceState, error) { + panic("fakeAuthContext Device is not implemented") +} + +func (*fakeAuthContext) UpdateDeviceAuth(*auth.DeviceState, string) (*auth.DeviceState, error) { + panic("fakeAuthContext UpdateDeviceAuth is not implemented") +} + +func (*fakeAuthContext) UpdateUserAuth(*auth.UserState, []string) (*auth.UserState, error) { + panic("fakeAuthContext UpdateUserAuth is not implemented") +} + +func (*fakeAuthContext) StoreID(string) (string, error) { + panic("fakeAuthContext StoreID is not implemented") +} + +func (*fakeAuthContext) DeviceSessionRequestParams(nonce string) (*auth.DeviceSessionRequestParams, error) { + panic("fakeAuthContext DeviceSessionRequestParams is not implemented") +} + +type storeStateSuite struct{} + +var _ = Suite(&storeStateSuite{}) + +func (ss *storeStateSuite) state(c *C, data string) *state.State { + if data == "" { + return state.New(nil) + } + + tmpdir := c.MkDir() + state_json := filepath.Join(tmpdir, "state.json") + c.Assert(ioutil.WriteFile(state_json, []byte(data), 0600), IsNil) + + r, err := os.Open(state_json) + c.Assert(err, IsNil) + defer r.Close() + st, err := state.ReadState(nil, r) + c.Assert(err, IsNil) + + return st +} + +func (ss *storeStateSuite) TestDefaultBaseURL(c *C) { + st := ss.state(c, "") + st.Lock() + baseURL := storestate.BaseURL(st) + st.Unlock() + c.Check(baseURL, Equals, "") +} + +func (ss *storeStateSuite) TestExplicitBaseURL(c *C) { + st := ss.state(c, "") + st.Lock() + defer st.Unlock() + + storeState := storestate.StoreState{BaseURL: "http://example.com/"} + st.Set("store", &storeState) + baseURL := storestate.BaseURL(st) + c.Check(baseURL, Equals, storeState.BaseURL) +} + +func (ss *storeStateSuite) TestSetupStoreCachesState(c *C) { + st := ss.state(c, "") + st.Lock() + defer st.Unlock() + + c.Check(func() { storestate.Store(st) }, PanicMatches, + "internal error: needing the store before managers have initialized it") + c.Check(func() { storestate.CachedAuthContext(st) }, PanicMatches, + "internal error: needing the auth context before managers have initialized it") + + err := storestate.SetupStore(st, &fakeAuthContext{}) + c.Assert(err, IsNil) + + c.Check(storestate.Store(st), NotNil) + c.Check(storestate.CachedAuthContext(st), NotNil) +} + +func (ss *storeStateSuite) TestSetupStoreDefaultBaseURL(c *C) { + var config *store.Config + defer storestate.MockStoreNew(func(c *store.Config, _ auth.AuthContext) *store.Store { + config = c + return nil + })() + + st := ss.state(c, "") + st.Lock() + defer st.Unlock() + + err := storestate.SetupStore(st, nil) + c.Assert(err, IsNil) + + c.Check(config.StoreBaseURL.String(), Equals, "https://api.snapcraft.io/") +} + +func (ss *storeStateSuite) TestSetupStoreBaseURLFromState(c *C) { + var config *store.Config + defer storestate.MockStoreNew(func(c *store.Config, _ auth.AuthContext) *store.Store { + config = c + return nil + })() + + st := ss.state(c, `{"data":{"store":{"base-url": "http://example.com/"}}}`) + st.Lock() + defer st.Unlock() + + err := storestate.SetupStore(st, nil) + c.Assert(err, IsNil) + + c.Check(config.StoreBaseURL.String(), Equals, "http://example.com/") +} + +func (ss *storeStateSuite) TestSetupStoreBadEnvironURLOverride(c *C) { + // We need store state to trigger this. + st := ss.state(c, `{"data":{"store":{"base-url": "http://example.com/"}}}`) + st.Lock() + defer st.Unlock() + + c.Assert(os.Setenv("SNAPPY_FORCE_API_URL", "://force-api.local/"), IsNil) + defer os.Setenv("SNAPPY_FORCE_API_URL", "") + + err := storestate.SetupStore(st, nil) + c.Assert(err, NotNil) + c.Check(err, ErrorMatches, "invalid SNAPPY_FORCE_API_URL: parse ://force-api.local/: missing protocol scheme") +} + +func (ss *storeStateSuite) TestSetupStoreEmptyBaseURLFromState(c *C) { + var config *store.Config + defer storestate.MockStoreNew(func(c *store.Config, _ auth.AuthContext) *store.Store { + config = c + return nil + })() + + st := ss.state(c, `{"data":{"store":{"base-url": ""}}}`) + st.Lock() + defer st.Unlock() + + err := storestate.SetupStore(st, nil) + c.Assert(err, IsNil) + + c.Check(config.StoreBaseURL.String(), Equals, "https://api.snapcraft.io/") +} + +func (ss *storeStateSuite) TestSetupStoreInvalidBaseURLFromState(c *C) { + st := ss.state(c, `{"data":{"store":{"base-url": "://example.com/"}}}`) + st.Lock() + defer st.Unlock() + + err := storestate.SetupStore(st, nil) + c.Assert(err, NotNil) + c.Check(err, ErrorMatches, "invalid store API base URL: parse ://example.com/: missing protocol scheme") +} + +func (ss *storeStateSuite) TestStore(c *C) { + st := ss.state(c, "") + st.Lock() + defer st.Unlock() + + sto := &store.Store{} + storestate.ReplaceStore(st, sto) + store1 := storestate.Store(st) + c.Check(store1, Equals, sto) + + // cached + store2 := storestate.Store(st) + c.Check(store2, Equals, sto) +} + +func (ss *storeStateSuite) TestSetBaseURL(c *C) { + st := ss.state(c, "") + st.Lock() + defer st.Unlock() + + err := storestate.SetupStore(st, &fakeAuthContext{}) + c.Assert(err, IsNil) + + oldStore := storestate.Store(st) + c.Assert(storestate.BaseURL(st), Equals, "") + + u, err := url.Parse("http://example.com/") + c.Assert(err, IsNil) + err = storestate.SetBaseURL(st, u) + c.Assert(err, IsNil) + + c.Check(storestate.Store(st), Not(Equals), oldStore) + c.Check(storestate.BaseURL(st), Equals, "http://example.com/") +} + +func (ss *storeStateSuite) TestSetBaseURLReset(c *C) { + st := ss.state(c, "") + st.Lock() + defer st.Unlock() + + st.Set("store", map[string]interface{}{ + "base-url": "http://example.com/", + }) + c.Assert(storestate.BaseURL(st), Not(Equals), "") + + err := storestate.SetupStore(st, &fakeAuthContext{}) + c.Assert(err, IsNil) + oldStore := storestate.Store(st) + + err = storestate.SetBaseURL(st, nil) + c.Assert(err, IsNil) + + c.Check(storestate.Store(st), Not(Equals), oldStore) + c.Check(storestate.BaseURL(st), Equals, "") +} + +func (ss *storeStateSuite) TestSetBaseURLBadEnvironURLOverride(c *C) { + c.Assert(os.Setenv("SNAPPY_FORCE_API_URL", "://force-api.local/"), IsNil) + defer os.Setenv("SNAPPY_FORCE_API_URL", "") + + u, _ := url.Parse("http://example.com/") + st := ss.state(c, "") + err := storestate.SetBaseURL(st, u) + c.Assert(err, NotNil) + c.Check(err, ErrorMatches, "invalid SNAPPY_FORCE_API_URL: parse ://force-api.local/: missing protocol scheme") +} diff -Nru snapd-2.28.5/packaging/arch/PKGBUILD snapd-2.29.3/packaging/arch/PKGBUILD --- snapd-2.28.5/packaging/arch/PKGBUILD 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/packaging/arch/PKGBUILD 2017-10-23 06:17:27.000000000 +0000 @@ -69,7 +69,7 @@ --libexecdir=/usr/lib/snapd \ --with-snap-mount-dir=/var/lib/snapd/snap \ --disable-apparmor \ - --enable-nvidia-arch \ + --enable-nvidia-biarch \ --enable-merged-usr make } diff -Nru snapd-2.28.5/packaging/fedora/snapd.spec snapd-2.29.3/packaging/fedora/snapd.spec --- snapd-2.28.5/packaging/fedora/snapd.spec 2017-10-13 21:25:46.000000000 +0000 +++ snapd-2.29.3/packaging/fedora/snapd.spec 2017-11-09 18:16:16.000000000 +0000 @@ -48,7 +48,7 @@ %global snappy_svcs snapd.service snapd.socket snapd.autoimport.service snapd.refresh.timer snapd.refresh.service Name: snapd -Version: 2.28.5 +Version: 2.29.3 Release: 0%{?dist} Summary: A transactional software package manager Group: System Environment/Base @@ -236,7 +236,6 @@ Provides: golang(%{import_path}/errtracker) = %{version}-%{release} Provides: golang(%{import_path}/httputil) = %{version}-%{release} Provides: golang(%{import_path}/i18n) = %{version}-%{release} -Provides: golang(%{import_path}/i18n/dumb) = %{version}-%{release} Provides: golang(%{import_path}/image) = %{version}-%{release} Provides: golang(%{import_path}/interfaces) = %{version}-%{release} Provides: golang(%{import_path}/interfaces/apparmor) = %{version}-%{release} @@ -366,9 +365,9 @@ %gobuild -o bin/snapd $GOFLAGS %{import_path}/cmd/snapd %gobuild -o bin/snap $GOFLAGS %{import_path}/cmd/snap %gobuild -o bin/snapctl $GOFLAGS %{import_path}/cmd/snapctl -%gobuild -o bin/snap-update-ns $GOFLAGS %{import_path}/cmd/snap-update-ns -# build snap-exec completely static for base snaps +# build snap-exec and snap-update-ns completely static for base snaps CGO_ENABLED=0 %gobuild -o bin/snap-exec $GOFLAGS %{import_path}/cmd/snap-exec +%gobuild -o bin/snap-update-ns --ldflags '-extldflags "-static"' $GOFLAGS %{import_path}/cmd/snap-update-ns # We don't need mvo5 fork for seccomp, as we have seccomp 2.3.x sed -e "s:github.com/mvo5/libseccomp-golang:github.com/seccomp/libseccomp-golang:g" -i cmd/snap-seccomp/*.go @@ -427,6 +426,7 @@ install -d -p %{buildroot}%{_sharedstatedir}/snapd/snaps install -d -p %{buildroot}%{_sharedstatedir}/snapd/snap/bin install -d -p %{buildroot}%{_localstatedir}/snap +install -d -p %{buildroot}%{_localstatedir}/cache/snapd install -d -p %{buildroot}%{_datadir}/selinux/devel/include/contrib install -d -p %{buildroot}%{_datadir}/selinux/packages @@ -574,6 +574,7 @@ %dir %{_sharedstatedir}/snapd/seccomp/bpf %dir %{_sharedstatedir}/snapd/snaps %dir %{_sharedstatedir}/snapd/snap +%dir /var/cache/snapd %ghost %dir %{_sharedstatedir}/snapd/snap/bin %dir %{_localstatedir}/snap %ghost %{_sharedstatedir}/snapd/state.json @@ -590,7 +591,7 @@ %{_libexecdir}/snapd/snap-seccomp %{_libexecdir}/snapd/snap-update-ns %{_libexecdir}/snapd/system-shutdown -%{_mandir}/man5/snap-confine.5* +%{_mandir}/man1/snap-confine.1* %{_mandir}/man5/snap-discard-ns.5* %{_prefix}/lib/udev/snappy-app-dev %{_udevrulesdir}/80-snappy-assign.rules @@ -658,6 +659,232 @@ %changelog +* Thu Nov 09 2017 Michael Vogt +- New upstream release 2.29.3 + - daemon: cherry-picked /v2/logs fixes + - cmd/snap-confine: Respect biarch nature of libdirs + - cmd/snap-confine: Ensure snap-confine is allowed to access os- + release + - interfaces: fix udev tagging for hooks + - cmd: fix re-exec bug with classic confinement for host snapd + - tests: disable xdg-open-compat test + - cmd/snap-confine: add slave PTYs and let devpts newinstance + perform mediation + - interfaces/many: misc policy updates for browser-support, cups- + control and network-status + - interfaces/raw-usb: match on SUBSYSTEM, not SUBSYSTEMS + - tests: fix security-device-cgroup* tests on devices with + framebuffer + +* Fri Nov 03 2017 Michael Vogt +- New upstream release 2.29.2 + - snapctl: disable stop/start/restart (2.29) + - cmd/snap-update-ns: fix collection of changes made + +* Fri Nov 03 2017 Michael Vogt +- New upstream release 2.29.1 + - interfaces: fix incorrect signature of ofono DBusPermanentSlot + - interfaces/serial-port: udev tag plugged slots that have just + 'path' via KERNEL + - interfaces/hidraw: udev tag plugged slots that have just 'path' + via KERNEL + - interfaces/uhid: unconditionally add existing uhid device to the + device cgroup + - cmd/snap-update-ns: fix mount rules for font sharing + - tests: disable refresh-undo test on trusty for now + - tests: use `snap change --last=install` in snapd-reexec test + - Revert " wrappers: fail install if exec-line cannot be re-written + - interfaces: don't udev tag devmode or classic snaps + - many: make ignore-validation sticky and send the flag with refresh + requests + +* Mon Oct 30 2017 Michael Vogt +- New upstream release 2.29 + - interfaces/many: miscellaneous updates based on feedback from the + field + - snap-confine: allow reading uevents from any where in /sys + - spread: add bionic beaver + - debian: make packaging/ubuntu-14.04/copyright a real file again + - tests: cherry pick the fix for services test into 2.29 + - cmd/snap-update-ns: initialize logger + - hooks/configure: queue service restarts + - snap-{confine,seccomp}: make @unrestricted fully unrestricted + - interfaces: clean system apparmor cache on core device + - debian: do not build static snap-exec on powerpc + - snap-confine: increase sanity_timeout to 6s + - snapctl: cherry pick service commands changes + - cmd/snap: tell translators about arg names and descs req's + - systemd: run all mount units before snapd.service to avoid race + - store: add a test to show auth failures are forwarded by doRequest + - daemon: convert ErrInvalidCredentials to a 401 Unauthorized error. + - store: forward on INVALID_CREDENTIALS error as + ErrInvalidCredentials + - daemon: generate a forbidden response message if polkit dialog is + dismissed + - daemon: Allow Polkit authorization to cancel changes. + - travis: switch to container based test runs + - interfaces: reduce duplicated code in interface tests mocks + - tests: improve revert related testing + - interfaces: sanitize plugs and slots early in ReadInfo + - store: add download caching + - preserve TMPDIR and HOSTALIASES across snap-confine invocation + - snap-confine: init all arrays with `= {0,}` + - tests: adding test for network-manager interface + - interfaces/mount: don't generate legacy per-hook/per-app mount + profiles + - snap: introduce structured epochs + - tests: fix interfaces-cups-control test for cups-2.2.5 + - snap-confine: cleanup incorrectly created nvidia udev tags + - cmd/snap-confine: update valid security tag regexp + - cmd/libsnap: enable two stranded tests + - cmd,packaging: enable apparmor on openSUSE + - overlord/ifacestate: refresh all security backends on startup + - interfaces/dbus: drop unneeded check for + release.ReleaseInfo.ForceDevMode + - dbus: ensure io.snapcraft.Launcher.service is created on re- + exec + - overlord/auth: continue for now supporting UBUNTU_STORE_ID if the + model is generic-classic + - snap-confine: add support for handling /dev/nvidia-modeset + - interfaces/network-control: remove incorrect rules for tun + - spread: allow setting SPREAD_DEBUG_EACH=0 to disable debug-each + section + - packaging: remove .mnt files on removal + - tests: fix econnreset scenario when the iptables rule was not + created + - tests: add test for lxd interface + - run-checks: use nakedret static checker to check for naked + returns on long functions + - progress: be more flexible in testing ansimeter + - interfaces: fix udev rules for tun + - many: implement our own ANSI-escape-using progress indicator + - snap-exec: update tests to follow main_test pattern + - snap: support "command: foo $ENV_STRING" + - packaging: update nvidia configure options + - snap: add new `snap pack` and use in tests + - cmd: correctly name the "Ubuntu" and "Arch" NVIDIA methods + - cmd: add autogen case for solus + - tests: do not use http://canihazip.com/ which appears to be down + - hooks: commands for controlling own services from snapctl + - snap: refactor cmdGet.Execute() + - interfaces/mount: make Change.Perform testable and test it + - interfaces/mount,cmd/snap-update-ns: move change code + - snap-confine: is_running_on_classic_distribution() looks into os- + release + - interfaces: misc updates for default, browser-support, home and + system-observe + - interfaces: deny lttng by default + - interfaces/lxd: lxd slot implementation can also be an app snap + - release,cmd,dirs: Redo the distro checks to take into account + distribution families + - cmd/snap: completion for alias and unalias + - snap-confine: add new SC_CLEANUP and use it + - snap: refrain from running filepath.Base on random strings + - cmd/snap-confine: put processes into freezer hierarchy + - wrappers: fail install if exec-line cannot be re-written + - cmd/snap-seccomp,osutil: make user/group lookup functions public + - snapstate: deal with snap user data in the /root/ directory + - interfaces: Enhance full-confinement support for biarch + distributions + - snap-confine: Only attempt to copy/mount NVIDIA libs when NVIDIA + is used + - packaging/fedora: Add Fedora 26, 27, and Rawhide symlinks + - overlord/snapstate: prefer a smaller corner case for doing the + wrong thing + - cmd/snap-repair: set user agent for snap-repair http requests + - packaging: bring down the delta between 14.04 and 16.04 + - snap-confine: Ensure lib64 biarch directory is respected + - snap-confine: update apparmor rules for fedora based base snaps + - tests: Increase SNAPD_CONFIGURE_HOOK_TIMEOUT to 3 minutes to + install real snaps + - daemon: use client.Snap instead of map[string]interface{} for + snaps. + - hooks: rename refresh hook to post-refresh + - git: make the .gitingore file a bit more targeted + - interfaces/opengl: don't udev tag nvidia devices and use snap- + confine instead + - cmd/snap-{confine,update-ns}: apply mount profiles using snap- + update-ns + - cmd: update "make hack" + - interfaces/system-observe: allow clients to enumerate DBus + connection names + - snap-repair: implement `snap-repair {list,show}` + - dirs,interfaces: create snap-confine.d on demand when re-executing + - snap-confine: fix base snaps on core + - cmd/snap-repair: fix tests when running as root + - interfaces: add Connection type + - cmd/snap-repair: skip disabled repairs + - cmd/snap-repair: prefer leaking unmanaged fds on test failure over + closing random ones + - snap-repair: make `repair` binary available for repair scripts + - snap-repair: fix missing Close() in TestStatusHappy + - cmd/snap-confine,packaging: import snapd-generated policy + - cmd/snap: return empty document if snap has no configuration + - snap-seccomp: run secondary-arch tests via gcc-multilib + - snap: implement `snap {repair,repairs}` and pass-through to snap- + repair + - interfaces/builtin: allow receiving dbus messages + - snap-repair: implement `snap-repair {done,skip,retry}` + - data/completion: small tweak to snap completion snippet + - dirs: fix classic support detection + - cmd/snap-repair: integrate root public keys for repairs + - tests: fix ubuntu core services + - tests: add new test that checks that the compat snapd-xdg-open + works + - snap-confine: improve error message if core/u-core cannot be found + - tests: only run tests/regression/nmcli on amd64 + - interfaces: mount host system fonts in desktop interface + - interfaces: enable partial apparmor support + - snapstate: auto-install missing base snaps + - spread: work around temporary packaging issue in debian sid + - asserts,cmd/snap-repair: introduce a mandatory summary for repairs + - asserts,cmd/snap-repair: represent RepairID internally as an int + - tests: test the real "xdg-open" from the core snap + - many: implement fetching sections and package names periodically. + - interfaces/network: allow using netcat as client + - snap-seccomp, osutil: use osutil.AtomicFile in snap-seccomp + - snap-seccomp: skip mknod syscall on arm64 + - tests: add trivial canonical-livepatch test + - tests: add test that ensures that all core services are working + - many: add logger.MockLogger() and use it in the tests + - snap-repair: fix test failure in TestRepairHitsTimeout + - asserts: add empty values check in HeadersFromPrimaryKey + - daemon: remove unused installSnap var in test + - daemon: reach for Overlord.Loop less thanks to overlord.Mock + - snap-seccomp: manually resolve socket() call in tests + - tests: change regex used to validate installed ubuntu core snap + - cmd/snapctl: allow snapctl -h without a context (regression fix). + - many: use snapcore/snapd/i18n instead of i18n/dumb + - many: introduce asserts.NotFoundError replacing both ErrNotFound + and store.AssertionNotFoundError + - packaging: don't include any marcos in comments + - overlord: use overlord.Mock in more tests, make sure we check the + outcome of Settle + - tests: try to fix staging tests + - store: simplify api base url config + - systemd: add systemd.MockJournalctl() + - many: provide systemd.MockSystemctl() helper + - tests: improve the listing test to not fail for e.g. 2.28~rc2 + - snapstate: give snapmgrTestSuite.settle() more time to settle + - tests: fix regex to check core version on snap list + - debian: update trusted account-keys check on 14.04 packaging + - interfaces: add udev netlink support to hardware-observe + - overlord: introduce Mock which enables to use Overlord.Settle for + settle in many more places + - snap-repair: execute the repair and capture logs/status + - tests: run the tests/unit/go everywhere + - daemon, snapstate: move ensureCore from daemon/api.go into + snapstate.go + - cmd/snap: get keys or root document + - spread.yaml: turn suse to manual given that it's breaking master + - many: configure store from state, reconfigure store at runtime + - osutil: AtomicWriter (an io.Writer), and io.Reader versions of + AtomicWrite* + - tests: check for negative syscalls in runBpf() and skip those + tests + - docs: use abolute path in PULL_REQUEST_TEMPLATE.md + - store: move device auth endpoint uris to config (#3831) + * Fri Oct 13 2017 Michael Vogt - New upstream release 2.28.5 - snap-confine: cleanup broken nvidia udev tags diff -Nru snapd-2.28.5/packaging/fedora/snap-mgmt.sh snapd-2.29.3/packaging/fedora/snap-mgmt.sh --- snapd-2.28.5/packaging/fedora/snap-mgmt.sh 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/packaging/fedora/snap-mgmt.sh 2017-10-23 06:17:27.000000000 +0000 @@ -89,6 +89,10 @@ # opportunistic as those might not be actually mounted for mnt in /run/snapd/ns/*.mnt; do umount -l "$mnt" || true + rm -f "$mnt" + done + for fstab in /run/snapd/ns/*.fstab; do + rm -f "$fstab" done umount -l /run/snapd/ns/ || true @@ -105,6 +109,9 @@ rm -rf /var/lib/snapd/seccomp/bpf/* rm -rf /var/lib/snapd/device/* rm -rf /var/lib/snapd/assertions/* + + echo "Removing snapd catalog cache" + rm -f /var/cache/snapd/* } while [ -n "$1" ]; do diff -Nru snapd-2.28.5/packaging/fedora-25/snapd.spec snapd-2.29.3/packaging/fedora-25/snapd.spec --- snapd-2.28.5/packaging/fedora-25/snapd.spec 2017-10-13 21:25:46.000000000 +0000 +++ snapd-2.29.3/packaging/fedora-25/snapd.spec 2017-11-09 18:16:16.000000000 +0000 @@ -48,7 +48,7 @@ %global snappy_svcs snapd.service snapd.socket snapd.autoimport.service snapd.refresh.timer snapd.refresh.service Name: snapd -Version: 2.28.5 +Version: 2.29.3 Release: 0%{?dist} Summary: A transactional software package manager Group: System Environment/Base @@ -236,7 +236,6 @@ Provides: golang(%{import_path}/errtracker) = %{version}-%{release} Provides: golang(%{import_path}/httputil) = %{version}-%{release} Provides: golang(%{import_path}/i18n) = %{version}-%{release} -Provides: golang(%{import_path}/i18n/dumb) = %{version}-%{release} Provides: golang(%{import_path}/image) = %{version}-%{release} Provides: golang(%{import_path}/interfaces) = %{version}-%{release} Provides: golang(%{import_path}/interfaces/apparmor) = %{version}-%{release} @@ -366,9 +365,9 @@ %gobuild -o bin/snapd $GOFLAGS %{import_path}/cmd/snapd %gobuild -o bin/snap $GOFLAGS %{import_path}/cmd/snap %gobuild -o bin/snapctl $GOFLAGS %{import_path}/cmd/snapctl -%gobuild -o bin/snap-update-ns $GOFLAGS %{import_path}/cmd/snap-update-ns -# build snap-exec completely static for base snaps +# build snap-exec and snap-update-ns completely static for base snaps CGO_ENABLED=0 %gobuild -o bin/snap-exec $GOFLAGS %{import_path}/cmd/snap-exec +%gobuild -o bin/snap-update-ns --ldflags '-extldflags "-static"' $GOFLAGS %{import_path}/cmd/snap-update-ns # We don't need mvo5 fork for seccomp, as we have seccomp 2.3.x sed -e "s:github.com/mvo5/libseccomp-golang:github.com/seccomp/libseccomp-golang:g" -i cmd/snap-seccomp/*.go @@ -427,6 +426,7 @@ install -d -p %{buildroot}%{_sharedstatedir}/snapd/snaps install -d -p %{buildroot}%{_sharedstatedir}/snapd/snap/bin install -d -p %{buildroot}%{_localstatedir}/snap +install -d -p %{buildroot}%{_localstatedir}/cache/snapd install -d -p %{buildroot}%{_datadir}/selinux/devel/include/contrib install -d -p %{buildroot}%{_datadir}/selinux/packages @@ -574,6 +574,7 @@ %dir %{_sharedstatedir}/snapd/seccomp/bpf %dir %{_sharedstatedir}/snapd/snaps %dir %{_sharedstatedir}/snapd/snap +%dir /var/cache/snapd %ghost %dir %{_sharedstatedir}/snapd/snap/bin %dir %{_localstatedir}/snap %ghost %{_sharedstatedir}/snapd/state.json @@ -590,7 +591,7 @@ %{_libexecdir}/snapd/snap-seccomp %{_libexecdir}/snapd/snap-update-ns %{_libexecdir}/snapd/system-shutdown -%{_mandir}/man5/snap-confine.5* +%{_mandir}/man1/snap-confine.1* %{_mandir}/man5/snap-discard-ns.5* %{_prefix}/lib/udev/snappy-app-dev %{_udevrulesdir}/80-snappy-assign.rules @@ -658,6 +659,232 @@ %changelog +* Thu Nov 09 2017 Michael Vogt +- New upstream release 2.29.3 + - daemon: cherry-picked /v2/logs fixes + - cmd/snap-confine: Respect biarch nature of libdirs + - cmd/snap-confine: Ensure snap-confine is allowed to access os- + release + - interfaces: fix udev tagging for hooks + - cmd: fix re-exec bug with classic confinement for host snapd + - tests: disable xdg-open-compat test + - cmd/snap-confine: add slave PTYs and let devpts newinstance + perform mediation + - interfaces/many: misc policy updates for browser-support, cups- + control and network-status + - interfaces/raw-usb: match on SUBSYSTEM, not SUBSYSTEMS + - tests: fix security-device-cgroup* tests on devices with + framebuffer + +* Fri Nov 03 2017 Michael Vogt +- New upstream release 2.29.2 + - snapctl: disable stop/start/restart (2.29) + - cmd/snap-update-ns: fix collection of changes made + +* Fri Nov 03 2017 Michael Vogt +- New upstream release 2.29.1 + - interfaces: fix incorrect signature of ofono DBusPermanentSlot + - interfaces/serial-port: udev tag plugged slots that have just + 'path' via KERNEL + - interfaces/hidraw: udev tag plugged slots that have just 'path' + via KERNEL + - interfaces/uhid: unconditionally add existing uhid device to the + device cgroup + - cmd/snap-update-ns: fix mount rules for font sharing + - tests: disable refresh-undo test on trusty for now + - tests: use `snap change --last=install` in snapd-reexec test + - Revert " wrappers: fail install if exec-line cannot be re-written + - interfaces: don't udev tag devmode or classic snaps + - many: make ignore-validation sticky and send the flag with refresh + requests + +* Mon Oct 30 2017 Michael Vogt +- New upstream release 2.29 + - interfaces/many: miscellaneous updates based on feedback from the + field + - snap-confine: allow reading uevents from any where in /sys + - spread: add bionic beaver + - debian: make packaging/ubuntu-14.04/copyright a real file again + - tests: cherry pick the fix for services test into 2.29 + - cmd/snap-update-ns: initialize logger + - hooks/configure: queue service restarts + - snap-{confine,seccomp}: make @unrestricted fully unrestricted + - interfaces: clean system apparmor cache on core device + - debian: do not build static snap-exec on powerpc + - snap-confine: increase sanity_timeout to 6s + - snapctl: cherry pick service commands changes + - cmd/snap: tell translators about arg names and descs req's + - systemd: run all mount units before snapd.service to avoid race + - store: add a test to show auth failures are forwarded by doRequest + - daemon: convert ErrInvalidCredentials to a 401 Unauthorized error. + - store: forward on INVALID_CREDENTIALS error as + ErrInvalidCredentials + - daemon: generate a forbidden response message if polkit dialog is + dismissed + - daemon: Allow Polkit authorization to cancel changes. + - travis: switch to container based test runs + - interfaces: reduce duplicated code in interface tests mocks + - tests: improve revert related testing + - interfaces: sanitize plugs and slots early in ReadInfo + - store: add download caching + - preserve TMPDIR and HOSTALIASES across snap-confine invocation + - snap-confine: init all arrays with `= {0,}` + - tests: adding test for network-manager interface + - interfaces/mount: don't generate legacy per-hook/per-app mount + profiles + - snap: introduce structured epochs + - tests: fix interfaces-cups-control test for cups-2.2.5 + - snap-confine: cleanup incorrectly created nvidia udev tags + - cmd/snap-confine: update valid security tag regexp + - cmd/libsnap: enable two stranded tests + - cmd,packaging: enable apparmor on openSUSE + - overlord/ifacestate: refresh all security backends on startup + - interfaces/dbus: drop unneeded check for + release.ReleaseInfo.ForceDevMode + - dbus: ensure io.snapcraft.Launcher.service is created on re- + exec + - overlord/auth: continue for now supporting UBUNTU_STORE_ID if the + model is generic-classic + - snap-confine: add support for handling /dev/nvidia-modeset + - interfaces/network-control: remove incorrect rules for tun + - spread: allow setting SPREAD_DEBUG_EACH=0 to disable debug-each + section + - packaging: remove .mnt files on removal + - tests: fix econnreset scenario when the iptables rule was not + created + - tests: add test for lxd interface + - run-checks: use nakedret static checker to check for naked + returns on long functions + - progress: be more flexible in testing ansimeter + - interfaces: fix udev rules for tun + - many: implement our own ANSI-escape-using progress indicator + - snap-exec: update tests to follow main_test pattern + - snap: support "command: foo $ENV_STRING" + - packaging: update nvidia configure options + - snap: add new `snap pack` and use in tests + - cmd: correctly name the "Ubuntu" and "Arch" NVIDIA methods + - cmd: add autogen case for solus + - tests: do not use http://canihazip.com/ which appears to be down + - hooks: commands for controlling own services from snapctl + - snap: refactor cmdGet.Execute() + - interfaces/mount: make Change.Perform testable and test it + - interfaces/mount,cmd/snap-update-ns: move change code + - snap-confine: is_running_on_classic_distribution() looks into os- + release + - interfaces: misc updates for default, browser-support, home and + system-observe + - interfaces: deny lttng by default + - interfaces/lxd: lxd slot implementation can also be an app snap + - release,cmd,dirs: Redo the distro checks to take into account + distribution families + - cmd/snap: completion for alias and unalias + - snap-confine: add new SC_CLEANUP and use it + - snap: refrain from running filepath.Base on random strings + - cmd/snap-confine: put processes into freezer hierarchy + - wrappers: fail install if exec-line cannot be re-written + - cmd/snap-seccomp,osutil: make user/group lookup functions public + - snapstate: deal with snap user data in the /root/ directory + - interfaces: Enhance full-confinement support for biarch + distributions + - snap-confine: Only attempt to copy/mount NVIDIA libs when NVIDIA + is used + - packaging/fedora: Add Fedora 26, 27, and Rawhide symlinks + - overlord/snapstate: prefer a smaller corner case for doing the + wrong thing + - cmd/snap-repair: set user agent for snap-repair http requests + - packaging: bring down the delta between 14.04 and 16.04 + - snap-confine: Ensure lib64 biarch directory is respected + - snap-confine: update apparmor rules for fedora based base snaps + - tests: Increase SNAPD_CONFIGURE_HOOK_TIMEOUT to 3 minutes to + install real snaps + - daemon: use client.Snap instead of map[string]interface{} for + snaps. + - hooks: rename refresh hook to post-refresh + - git: make the .gitingore file a bit more targeted + - interfaces/opengl: don't udev tag nvidia devices and use snap- + confine instead + - cmd/snap-{confine,update-ns}: apply mount profiles using snap- + update-ns + - cmd: update "make hack" + - interfaces/system-observe: allow clients to enumerate DBus + connection names + - snap-repair: implement `snap-repair {list,show}` + - dirs,interfaces: create snap-confine.d on demand when re-executing + - snap-confine: fix base snaps on core + - cmd/snap-repair: fix tests when running as root + - interfaces: add Connection type + - cmd/snap-repair: skip disabled repairs + - cmd/snap-repair: prefer leaking unmanaged fds on test failure over + closing random ones + - snap-repair: make `repair` binary available for repair scripts + - snap-repair: fix missing Close() in TestStatusHappy + - cmd/snap-confine,packaging: import snapd-generated policy + - cmd/snap: return empty document if snap has no configuration + - snap-seccomp: run secondary-arch tests via gcc-multilib + - snap: implement `snap {repair,repairs}` and pass-through to snap- + repair + - interfaces/builtin: allow receiving dbus messages + - snap-repair: implement `snap-repair {done,skip,retry}` + - data/completion: small tweak to snap completion snippet + - dirs: fix classic support detection + - cmd/snap-repair: integrate root public keys for repairs + - tests: fix ubuntu core services + - tests: add new test that checks that the compat snapd-xdg-open + works + - snap-confine: improve error message if core/u-core cannot be found + - tests: only run tests/regression/nmcli on amd64 + - interfaces: mount host system fonts in desktop interface + - interfaces: enable partial apparmor support + - snapstate: auto-install missing base snaps + - spread: work around temporary packaging issue in debian sid + - asserts,cmd/snap-repair: introduce a mandatory summary for repairs + - asserts,cmd/snap-repair: represent RepairID internally as an int + - tests: test the real "xdg-open" from the core snap + - many: implement fetching sections and package names periodically. + - interfaces/network: allow using netcat as client + - snap-seccomp, osutil: use osutil.AtomicFile in snap-seccomp + - snap-seccomp: skip mknod syscall on arm64 + - tests: add trivial canonical-livepatch test + - tests: add test that ensures that all core services are working + - many: add logger.MockLogger() and use it in the tests + - snap-repair: fix test failure in TestRepairHitsTimeout + - asserts: add empty values check in HeadersFromPrimaryKey + - daemon: remove unused installSnap var in test + - daemon: reach for Overlord.Loop less thanks to overlord.Mock + - snap-seccomp: manually resolve socket() call in tests + - tests: change regex used to validate installed ubuntu core snap + - cmd/snapctl: allow snapctl -h without a context (regression fix). + - many: use snapcore/snapd/i18n instead of i18n/dumb + - many: introduce asserts.NotFoundError replacing both ErrNotFound + and store.AssertionNotFoundError + - packaging: don't include any marcos in comments + - overlord: use overlord.Mock in more tests, make sure we check the + outcome of Settle + - tests: try to fix staging tests + - store: simplify api base url config + - systemd: add systemd.MockJournalctl() + - many: provide systemd.MockSystemctl() helper + - tests: improve the listing test to not fail for e.g. 2.28~rc2 + - snapstate: give snapmgrTestSuite.settle() more time to settle + - tests: fix regex to check core version on snap list + - debian: update trusted account-keys check on 14.04 packaging + - interfaces: add udev netlink support to hardware-observe + - overlord: introduce Mock which enables to use Overlord.Settle for + settle in many more places + - snap-repair: execute the repair and capture logs/status + - tests: run the tests/unit/go everywhere + - daemon, snapstate: move ensureCore from daemon/api.go into + snapstate.go + - cmd/snap: get keys or root document + - spread.yaml: turn suse to manual given that it's breaking master + - many: configure store from state, reconfigure store at runtime + - osutil: AtomicWriter (an io.Writer), and io.Reader versions of + AtomicWrite* + - tests: check for negative syscalls in runBpf() and skip those + tests + - docs: use abolute path in PULL_REQUEST_TEMPLATE.md + - store: move device auth endpoint uris to config (#3831) + * Fri Oct 13 2017 Michael Vogt - New upstream release 2.28.5 - snap-confine: cleanup broken nvidia udev tags diff -Nru snapd-2.28.5/packaging/fedora-25/snap-mgmt.sh snapd-2.29.3/packaging/fedora-25/snap-mgmt.sh --- snapd-2.28.5/packaging/fedora-25/snap-mgmt.sh 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/packaging/fedora-25/snap-mgmt.sh 2017-10-23 06:17:27.000000000 +0000 @@ -89,6 +89,10 @@ # opportunistic as those might not be actually mounted for mnt in /run/snapd/ns/*.mnt; do umount -l "$mnt" || true + rm -f "$mnt" + done + for fstab in /run/snapd/ns/*.fstab; do + rm -f "$fstab" done umount -l /run/snapd/ns/ || true @@ -105,6 +109,9 @@ rm -rf /var/lib/snapd/seccomp/bpf/* rm -rf /var/lib/snapd/device/* rm -rf /var/lib/snapd/assertions/* + + echo "Removing snapd catalog cache" + rm -f /var/cache/snapd/* } while [ -n "$1" ]; do diff -Nru snapd-2.28.5/packaging/fedora-26/snapd.spec snapd-2.29.3/packaging/fedora-26/snapd.spec --- snapd-2.28.5/packaging/fedora-26/snapd.spec 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/packaging/fedora-26/snapd.spec 2017-11-09 18:16:16.000000000 +0000 @@ -0,0 +1,1630 @@ +# With Fedora, nothing is bundled. For everything else, bundling is used. +# To use bundled stuff, use "--with vendorized" on rpmbuild +%if 0%{?fedora} +%bcond_with vendorized +%else +%bcond_without vendorized +%endif + +# A switch to allow building the package with support for testkeys which +# are used for the spread test suite of snapd. +%bcond_with testkeys + +%global with_devel 1 +%global with_debug 1 +%global with_check 0 +%global with_unit_test 0 +%global with_test_keys 0 + +# For the moment, we don't support all golang arches... +%global with_goarches 0 + +%if ! %{with vendorized} +%global with_bundled 0 +%else +%global with_bundled 1 +%endif + +%if ! %{with testkeys} +%global with_test_keys 0 +%else +%global with_test_keys 1 +%endif + +%if 0%{?with_debug} +%global _dwz_low_mem_die_limit 0 +%else +%global debug_package %{nil} +%endif + +%global provider github +%global provider_tld com +%global project snapcore +%global repo snapd +# https://github.com/snapcore/snapd +%global provider_prefix %{provider}.%{provider_tld}/%{project}/%{repo} +%global import_path %{provider_prefix} + +%global snappy_svcs snapd.service snapd.socket snapd.autoimport.service snapd.refresh.timer snapd.refresh.service + +Name: snapd +Version: 2.29.3 +Release: 0%{?dist} +Summary: A transactional software package manager +Group: System Environment/Base +License: GPLv3 +URL: https://%{provider_prefix} +%if ! 0%{?with_bundled} +Source0: https://%{provider_prefix}/archive/%{version}/%{name}-%{version}.tar.gz +%else +Source0: https://%{provider_prefix}/releases/download/%{version}/%{name}_%{version}.vendor.orig.tar.xz +%endif + +%if 0%{?with_goarches} +# e.g. el6 has ppc64 arch without gcc-go, so EA tag is required +ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}} +%else +# Verified arches from snapd upstream +ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 ppc64le s390x +%endif + +# If go_compiler is not set to 1, there is no virtual provide. Use golang instead. +BuildRequires: %{?go_compiler:compiler(go-compiler)}%{!?go_compiler:golang} +BuildRequires: systemd +%{?systemd_requires} + +Requires: snap-confine%{?_isa} = %{version}-%{release} +Requires: squashfs-tools +# we need squashfs.ko loaded +Requires: kmod(squashfs.ko) +# bash-completion owns /usr/share/bash-completion/completions +Requires: bash-completion + +# Force the SELinux module to be installed +Requires: %{name}-selinux = %{version}-%{release} + +%if ! 0%{?with_bundled} +BuildRequires: golang(github.com/cheggaaa/pb) +BuildRequires: golang(github.com/coreos/go-systemd/activation) +BuildRequires: golang(github.com/godbus/dbus) +BuildRequires: golang(github.com/godbus/dbus/introspect) +BuildRequires: golang(github.com/gorilla/mux) +BuildRequires: golang(github.com/jessevdk/go-flags) +BuildRequires: golang(github.com/mvo5/uboot-go/uenv) +BuildRequires: golang(github.com/ojii/gettext.go) +BuildRequires: golang(github.com/seccomp/libseccomp-golang) +BuildRequires: golang(golang.org/x/crypto/openpgp/armor) +BuildRequires: golang(golang.org/x/crypto/openpgp/packet) +BuildRequires: golang(golang.org/x/crypto/sha3) +BuildRequires: golang(golang.org/x/crypto/ssh/terminal) +BuildRequires: golang(golang.org/x/net/context) +BuildRequires: golang(golang.org/x/net/context/ctxhttp) +BuildRequires: golang(gopkg.in/check.v1) +BuildRequires: golang(gopkg.in/macaroon.v1) +BuildRequires: golang(gopkg.in/mgo.v2/bson) +BuildRequires: golang(gopkg.in/retry.v1) +BuildRequires: golang(gopkg.in/tomb.v2) +BuildRequires: golang(gopkg.in/yaml.v2) +%endif + +%description +Snappy is a modern, cross-distribution, transactional package manager +designed for working with self-contained, immutable packages. + +%package -n snap-confine +Summary: Confinement system for snap applications +License: GPLv3 +Group: System Environment/Base +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: libtool +BuildRequires: gcc +BuildRequires: gettext +BuildRequires: gnupg +BuildRequires: indent +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(libcap) +BuildRequires: pkgconfig(libseccomp) +BuildRequires: pkgconfig(libudev) +BuildRequires: pkgconfig(systemd) +BuildRequires: pkgconfig(udev) +BuildRequires: xfsprogs-devel +BuildRequires: glibc-static +BuildRequires: libseccomp-static +BuildRequires: valgrind +BuildRequires: %{_bindir}/rst2man +%if 0%{?fedora} >= 25 +# ShellCheck in F24 and older doesn't work +BuildRequires: %{_bindir}/shellcheck +%endif + +# Ensures older version from split packaging is replaced +Obsoletes: snap-confine < 2.19 + +%description -n snap-confine +This package is used internally by snapd to apply confinement to +the started snap applications. + +%package selinux +Summary: SELinux module for snapd +Group: System Environment/Base +License: GPLv2+ +BuildArch: noarch +BuildRequires: selinux-policy, selinux-policy-devel +Requires(post): selinux-policy-base >= %{_selinux_policy_version} +Requires(post): policycoreutils +Requires(post): policycoreutils-python-utils +Requires(pre): libselinux-utils +Requires(post): libselinux-utils + +%description selinux +This package provides the SELinux policy module to ensure snapd +runs properly under an environment with SELinux enabled. + + +%if 0%{?with_devel} +%package devel +Summary: %{summary} +BuildArch: noarch + +%if 0%{?with_check} && ! 0%{?with_bundled} +%endif + +%if ! 0%{?with_bundled} +Requires: golang(github.com/cheggaaa/pb) +Requires: golang(github.com/coreos/go-systemd/activation) +Requires: golang(github.com/godbus/dbus) +Requires: golang(github.com/godbus/dbus/introspect) +Requires: golang(github.com/gorilla/mux) +Requires: golang(github.com/jessevdk/go-flags) +Requires: golang(github.com/mvo5/uboot-go/uenv) +Requires: golang(github.com/ojii/gettext.go) +Requires: golang(github.com/seccomp/libseccomp-golang) +Requires: golang(golang.org/x/crypto/openpgp/armor) +Requires: golang(golang.org/x/crypto/openpgp/packet) +Requires: golang(golang.org/x/crypto/sha3) +Requires: golang(golang.org/x/crypto/ssh/terminal) +Requires: golang(golang.org/x/net/context) +Requires: golang(golang.org/x/net/context/ctxhttp) +Requires: golang(gopkg.in/check.v1) +Requires: golang(gopkg.in/macaroon.v1) +Requires: golang(gopkg.in/mgo.v2/bson) +Requires: golang(gopkg.in/retry.v1) +Requires: golang(gopkg.in/tomb.v2) +Requires: golang(gopkg.in/yaml.v2) +%else +# These Provides are unversioned because the sources in +# the bundled tarball are unversioned (they go by git commit) +# *sigh*... I hate golang... +Provides: bundled(golang(github.com/cheggaaa/pb)) +Provides: bundled(golang(github.com/coreos/go-systemd/activation)) +Provides: bundled(golang(github.com/godbus/dbus)) +Provides: bundled(golang(github.com/godbus/dbus/introspect)) +Provides: bundled(golang(github.com/gorilla/mux)) +Provides: bundled(golang(github.com/jessevdk/go-flags)) +Provides: bundled(golang(github.com/mvo5/uboot-go/uenv)) +Provides: bundled(golang(github.com/mvo5/libseccomp-golang)) +Provides: bundled(golang(github.com/ojii/gettext.go)) +Provides: bundled(golang(golang.org/x/crypto/openpgp/armor)) +Provides: bundled(golang(golang.org/x/crypto/openpgp/packet)) +Provides: bundled(golang(golang.org/x/crypto/sha3)) +Provides: bundled(golang(golang.org/x/crypto/ssh/terminal)) +Provides: bundled(golang(golang.org/x/net/context)) +Provides: bundled(golang(golang.org/x/net/context/ctxhttp)) +Provides: bundled(golang(gopkg.in/check.v1)) +Provides: bundled(golang(gopkg.in/macaroon.v1)) +Provides: bundled(golang(gopkg.in/mgo.v2/bson)) +Provides: bundled(golang(gopkg.in/retry.v1)) +Provides: bundled(golang(gopkg.in/tomb.v2)) +Provides: bundled(golang(gopkg.in/yaml.v2)) +%endif + +# Generated by gofed +Provides: golang(%{import_path}/arch) = %{version}-%{release} +Provides: golang(%{import_path}/asserts) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/assertstest) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/signtool) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/snapasserts) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/sysdb) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/systestkeys) = %{version}-%{release} +Provides: golang(%{import_path}/boot) = %{version}-%{release} +Provides: golang(%{import_path}/boot/boottest) = %{version}-%{release} +Provides: golang(%{import_path}/client) = %{version}-%{release} +Provides: golang(%{import_path}/cmd) = %{version}-%{release} +Provides: golang(%{import_path}/daemon) = %{version}-%{release} +Provides: golang(%{import_path}/dirs) = %{version}-%{release} +Provides: golang(%{import_path}/errtracker) = %{version}-%{release} +Provides: golang(%{import_path}/httputil) = %{version}-%{release} +Provides: golang(%{import_path}/i18n) = %{version}-%{release} +Provides: golang(%{import_path}/image) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/apparmor) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/backends) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/builtin) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/dbus) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/ifacetest) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/kmod) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/mount) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/policy) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/seccomp) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/systemd) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/udev) = %{version}-%{release} +Provides: golang(%{import_path}/logger) = %{version}-%{release} +Provides: golang(%{import_path}/osutil) = %{version}-%{release} +Provides: golang(%{import_path}/overlord) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/assertstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/auth) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/cmdstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/configstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/configstate/config) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/devicestate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/hookstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/hookstate/ctlcmd) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/hookstate/hooktest) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/ifacestate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/patch) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/snapstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/snapstate/backend) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/state) = %{version}-%{release} +Provides: golang(%{import_path}/partition) = %{version}-%{release} +Provides: golang(%{import_path}/partition/androidbootenv) = %{version}-%{release} +Provides: golang(%{import_path}/partition/grubenv) = %{version}-%{release} +Provides: golang(%{import_path}/partition/ubootenv) = %{version}-%{release} +Provides: golang(%{import_path}/progress) = %{version}-%{release} +Provides: golang(%{import_path}/release) = %{version}-%{release} +Provides: golang(%{import_path}/snap) = %{version}-%{release} +Provides: golang(%{import_path}/snap/snapdir) = %{version}-%{release} +Provides: golang(%{import_path}/snap/snapenv) = %{version}-%{release} +Provides: golang(%{import_path}/snap/snaptest) = %{version}-%{release} +Provides: golang(%{import_path}/snap/squashfs) = %{version}-%{release} +Provides: golang(%{import_path}/store) = %{version}-%{release} +Provides: golang(%{import_path}/strutil) = %{version}-%{release} +Provides: golang(%{import_path}/systemd) = %{version}-%{release} +Provides: golang(%{import_path}/tests/lib/fakestore/refresh) = %{version}-%{release} +Provides: golang(%{import_path}/tests/lib/fakestore/store) = %{version}-%{release} +Provides: golang(%{import_path}/testutil) = %{version}-%{release} +Provides: golang(%{import_path}/timeout) = %{version}-%{release} +Provides: golang(%{import_path}/timeutil) = %{version}-%{release} +Provides: golang(%{import_path}/wrappers) = %{version}-%{release} +Provides: golang(%{import_path}/x11) = %{version}-%{release} + + +%description devel +%{summary} + +This package contains library source intended for +building other packages which use import path with +%{import_path} prefix. +%endif + +%if 0%{?with_unit_test} && 0%{?with_devel} +%package unit-test-devel +Summary: Unit tests for %{name} package + +%if 0%{?with_check} +#Here comes all BuildRequires: PACKAGE the unit tests +#in %%check section need for running +%endif + +%if 0%{?with_check} && ! 0%{?with_bundled} +BuildRequires: golang(github.com/mvo5/goconfigparser) +%endif + +%if ! 0%{?with_bundled} +Requires: golang(github.com/mvo5/goconfigparser) +%else +Provides: bundled(golang(github.com/mvo5/goconfigparser)) +%endif + +# test subpackage tests code from devel subpackage +Requires: %{name}-devel = %{version}-%{release} + +%description unit-test-devel +%{summary} + +This package contains unit tests for project +providing packages with %{import_path} prefix. +%endif + +%prep +%setup -q + +%if ! 0%{?with_bundled} +# Ensure there's no bundled stuff accidentally leaking in... +rm -rf vendor/* + +# XXX: HACK: Fake that we have the right import path because bad testing +# did not verify that this path was actually valid on all supported systems. +mkdir -p vendor/gopkg.in/cheggaaa +ln -s %{gopath}/src/github.com/cheggaaa/pb vendor/gopkg.in/cheggaaa/pb.v1 + +%endif + +%build +# Generate version files +./mkversion.sh "%{version}-%{release}" + +# Build snapd +mkdir -p src/github.com/snapcore +ln -s ../../../ src/github.com/snapcore/snapd + +%if ! 0%{?with_bundled} +export GOPATH=$(pwd):%{gopath} +%else +export GOPATH=$(pwd):$(pwd)/Godeps/_workspace:%{gopath} +%endif + +GOFLAGS= +%if 0%{?with_test_keys} +GOFLAGS="$GOFLAGS -tags withtestkeys" +%endif + +# We have to build snapd first to prevent the build from +# building various things from the tree without additional +# set tags. +%gobuild -o bin/snapd $GOFLAGS %{import_path}/cmd/snapd +%gobuild -o bin/snap $GOFLAGS %{import_path}/cmd/snap +%gobuild -o bin/snapctl $GOFLAGS %{import_path}/cmd/snapctl +# build snap-exec and snap-update-ns completely static for base snaps +CGO_ENABLED=0 %gobuild -o bin/snap-exec $GOFLAGS %{import_path}/cmd/snap-exec +%gobuild -o bin/snap-update-ns --ldflags '-extldflags "-static"' $GOFLAGS %{import_path}/cmd/snap-update-ns + +# We don't need mvo5 fork for seccomp, as we have seccomp 2.3.x +sed -e "s:github.com/mvo5/libseccomp-golang:github.com/seccomp/libseccomp-golang:g" -i cmd/snap-seccomp/*.go +%gobuild -o bin/snap-seccomp $GOFLAGS %{import_path}/cmd/snap-seccomp + +# Build SELinux module +pushd ./data/selinux +make SHARE="%{_datadir}" TARGETS="snappy" +popd + +# Build snap-confine +pushd ./cmd +# FIXME This is a hack to get rid of a patch we have to ship for the +# Fedora package at the moment as /usr/lib/rpm/redhat/redhat-hardened-ld +# accidentially adds -pie for static executables. See +# https://bugzilla.redhat.com/show_bug.cgi?id=1343892 for a few more +# details. To prevent this from happening we drop the linker +# script and define our LDFLAGS manually for now. +export LDFLAGS="-Wl,-z,relro -z now" +autoreconf --force --install --verbose +# selinux support is not yet available, for now just disable apparmor +# FIXME: add --enable-caps-over-setuid as soon as possible (setuid discouraged!) +%configure \ + --disable-apparmor \ + --libexecdir=%{_libexecdir}/snapd/ \ + --with-snap-mount-dir=%{_sharedstatedir}/snapd/snap \ + --with-merged-usr + +%make_build +popd + +# Build systemd units +pushd ./data/ +make BINDIR="%{_bindir}" LIBEXECDIR="%{_libexecdir}" \ + SYSTEMDSYSTEMUNITDIR="%{_unitdir}" \ + SNAP_MOUNT_DIR="%{_sharedstatedir}/snapd/snap" \ + SNAPD_ENVIRONMENT_FILE="%{_sysconfdir}/sysconfig/snapd" +popd + +# Build environ-tweaking snippet +make -C data/env SNAP_MOUNT_DIR="%{_sharedstatedir}/snapd/snap" + +%install +install -d -p %{buildroot}%{_bindir} +install -d -p %{buildroot}%{_libexecdir}/snapd +install -d -p %{buildroot}%{_mandir}/man1 +install -d -p %{buildroot}%{_unitdir} +install -d -p %{buildroot}%{_sysconfdir}/profile.d +install -d -p %{buildroot}%{_sysconfdir}/sysconfig +install -d -p %{buildroot}%{_sharedstatedir}/snapd/assertions +install -d -p %{buildroot}%{_sharedstatedir}/snapd/desktop/applications +install -d -p %{buildroot}%{_sharedstatedir}/snapd/device +install -d -p %{buildroot}%{_sharedstatedir}/snapd/hostfs +install -d -p %{buildroot}%{_sharedstatedir}/snapd/mount +install -d -p %{buildroot}%{_sharedstatedir}/snapd/seccomp/bpf +install -d -p %{buildroot}%{_sharedstatedir}/snapd/snaps +install -d -p %{buildroot}%{_sharedstatedir}/snapd/snap/bin +install -d -p %{buildroot}%{_localstatedir}/snap +install -d -p %{buildroot}%{_localstatedir}/cache/snapd +install -d -p %{buildroot}%{_datadir}/selinux/devel/include/contrib +install -d -p %{buildroot}%{_datadir}/selinux/packages + +# Install snap and snapd +install -p -m 0755 bin/snap %{buildroot}%{_bindir} +install -p -m 0755 bin/snap-exec %{buildroot}%{_libexecdir}/snapd +install -p -m 0755 bin/snapctl %{buildroot}%{_bindir}/snapctl +install -p -m 0755 bin/snapd %{buildroot}%{_libexecdir}/snapd +install -p -m 0755 bin/snap-update-ns %{buildroot}%{_libexecdir}/snapd +install -p -m 0755 bin/snap-seccomp %{buildroot}%{_libexecdir}/snapd + +# Install SELinux module +install -p -m 0644 data/selinux/snappy.if %{buildroot}%{_datadir}/selinux/devel/include/contrib +install -p -m 0644 data/selinux/snappy.pp.bz2 %{buildroot}%{_datadir}/selinux/packages + +# Install snap(1) man page +bin/snap help --man > %{buildroot}%{_mandir}/man1/snap.1 + +# Install the "info" data file with snapd version +install -m 644 -D data/info %{buildroot}%{_libexecdir}/snapd/info + +# Install bash completion for "snap" +install -m 644 -D data/completion/snap %{buildroot}%{_datadir}/bash-completion/completions/snap +install -m 644 -D data/completion/complete.sh %{buildroot}%{_libexecdir}/snapd +install -m 644 -D data/completion/etelpmoc.sh %{buildroot}%{_libexecdir}/snapd + +# Install snap-confine +pushd ./cmd +%make_install +# Undo the 0000 permissions, they are restored in the files section +chmod 0755 %{buildroot}%{_sharedstatedir}/snapd/void +# We don't use AppArmor +rm -rfv %{buildroot}%{_sysconfdir}/apparmor.d +# ubuntu-core-launcher is dead +rm -fv %{buildroot}%{_bindir}/ubuntu-core-launcher +popd + +# Install all systemd units +pushd ./data/ +%make_install SYSTEMDSYSTEMUNITDIR="%{_unitdir}" BINDIR="%{_bindir}" LIBEXECDIR="%{_libexecdir}" +# Remove snappy core specific units +rm -fv %{buildroot}%{_unitdir}/snapd.system-shutdown.service +rm -fv %{buildroot}%{_unitdir}/snapd.snap-repair.* +rm -fv %{buildroot}%{_unitdir}/snapd.core-fixup.* +popd + +# Remove snappy core specific scripts +rm %{buildroot}%{_libexecdir}/snapd/snapd.core-fixup.sh + +# Install environ-tweaking snippet +pushd ./data/env +%make_install +popd + +# Disable re-exec by default +echo 'SNAP_REEXEC=0' > %{buildroot}%{_sysconfdir}/sysconfig/snapd + +# Install snap management script +install -pm 0755 packaging/fedora/snap-mgmt.sh %{buildroot}%{_libexecdir}/snapd/snap-mgmt + +# Create state.json file to be ghosted +touch %{buildroot}%{_sharedstatedir}/snapd/state.json + +# source codes for building projects +%if 0%{?with_devel} +install -d -p %{buildroot}/%{gopath}/src/%{import_path}/ +echo "%%dir %%{gopath}/src/%%{import_path}/." >> devel.file-list +# find all *.go but no *_test.go files and generate devel.file-list +for file in $(find . -iname "*.go" -o -iname "*.s" \! -iname "*_test.go") ; do + echo "%%dir %%{gopath}/src/%%{import_path}/$(dirname $file)" >> devel.file-list + install -d -p %{buildroot}/%{gopath}/src/%{import_path}/$(dirname $file) + cp -pav $file %{buildroot}/%{gopath}/src/%{import_path}/$file + echo "%%{gopath}/src/%%{import_path}/$file" >> devel.file-list +done +%endif + +# testing files for this project +%if 0%{?with_unit_test} && 0%{?with_devel} +install -d -p %{buildroot}/%{gopath}/src/%{import_path}/ +# find all *_test.go files and generate unit-test.file-list +for file in $(find . -iname "*_test.go"); do + echo "%%dir %%{gopath}/src/%%{import_path}/$(dirname $file)" >> devel.file-list + install -d -p %{buildroot}/%{gopath}/src/%{import_path}/$(dirname $file) + cp -pav $file %{buildroot}/%{gopath}/src/%{import_path}/$file + echo "%%{gopath}/src/%%{import_path}/$file" >> unit-test-devel.file-list +done + +# Install additional testdata +install -d %{buildroot}/%{gopath}/src/%{import_path}/cmd/snap/test-data/ +cp -pav cmd/snap/test-data/* %{buildroot}/%{gopath}/src/%{import_path}/cmd/snap/test-data/ +echo "%%{gopath}/src/%%{import_path}/cmd/snap/test-data" >> unit-test-devel.file-list +%endif + +%if 0%{?with_devel} +sort -u -o devel.file-list devel.file-list +%endif + +%check +# snapd tests +%if 0%{?with_check} && 0%{?with_unit_test} && 0%{?with_devel} +%if ! 0%{?with_bundled} +export GOPATH=%{buildroot}/%{gopath}:%{gopath} +%else +export GOPATH=%{buildroot}/%{gopath}:$(pwd)/Godeps/_workspace:%{gopath} +%endif +%gotest %{import_path}/... +%endif + +# snap-confine tests (these always run!) +pushd ./cmd +make check +popd + +%files +#define license tag if not already defined +%{!?_licensedir:%global license %doc} +%license COPYING +%doc README.md docs/* +%{_bindir}/snap +%{_bindir}/snapctl +%dir %{_libexecdir}/snapd +%{_libexecdir}/snapd/snapd +%{_libexecdir}/snapd/snap-exec +%{_libexecdir}/snapd/info +%{_libexecdir}/snapd/snap-mgmt +%{_mandir}/man1/snap.1* +%{_datadir}/bash-completion/completions/snap +%{_libexecdir}/snapd/complete.sh +%{_libexecdir}/snapd/etelpmoc.sh +%{_sysconfdir}/profile.d/snapd.sh +%{_unitdir}/snapd.socket +%{_unitdir}/snapd.service +%{_unitdir}/snapd.autoimport.service +%{_unitdir}/snapd.refresh.service +%{_unitdir}/snapd.refresh.timer +%config(noreplace) %{_sysconfdir}/sysconfig/snapd +%dir %{_sharedstatedir}/snapd +%dir %{_sharedstatedir}/snapd/assertions +%dir %{_sharedstatedir}/snapd/desktop +%dir %{_sharedstatedir}/snapd/desktop/applications +%dir %{_sharedstatedir}/snapd/device +%dir %{_sharedstatedir}/snapd/hostfs +%dir %{_sharedstatedir}/snapd/mount +%dir %{_sharedstatedir}/snapd/seccomp +%dir %{_sharedstatedir}/snapd/seccomp/bpf +%dir %{_sharedstatedir}/snapd/snaps +%dir %{_sharedstatedir}/snapd/snap +%dir /var/cache/snapd +%ghost %dir %{_sharedstatedir}/snapd/snap/bin +%dir %{_localstatedir}/snap +%ghost %{_sharedstatedir}/snapd/state.json +%{_datadir}/dbus-1/services/io.snapcraft.Launcher.service + +%files -n snap-confine +%doc cmd/snap-confine/PORTING +%license COPYING +%dir %{_libexecdir}/snapd +# For now, we can't use caps +# FIXME: Switch to "%%attr(0755,root,root) %%caps(cap_sys_admin=pe)" asap! +%attr(4755,root,root) %{_libexecdir}/snapd/snap-confine +%{_libexecdir}/snapd/snap-discard-ns +%{_libexecdir}/snapd/snap-seccomp +%{_libexecdir}/snapd/snap-update-ns +%{_libexecdir}/snapd/system-shutdown +%{_mandir}/man1/snap-confine.1* +%{_mandir}/man5/snap-discard-ns.5* +%{_prefix}/lib/udev/snappy-app-dev +%{_udevrulesdir}/80-snappy-assign.rules +%attr(0000,root,root) %{_sharedstatedir}/snapd/void + + +%files selinux +%license data/selinux/COPYING +%doc data/selinux/README.md +%{_datadir}/selinux/packages/snappy.pp.bz2 +%{_datadir}/selinux/devel/include/contrib/snappy.if + +%if 0%{?with_devel} +%files devel -f devel.file-list +%license COPYING +%doc README.md +%dir %{gopath}/src/%{provider}.%{provider_tld}/%{project} +%endif + +%if 0%{?with_unit_test} && 0%{?with_devel} +%files unit-test-devel -f unit-test-devel.file-list +%license COPYING +%doc README.md +%endif + +%post +%systemd_post %{snappy_svcs} +# If install, test if snapd socket and timer are enabled. +# If enabled, then attempt to start them. This will silently fail +# in chroots or other environments where services aren't expected +# to be started. +if [ $1 -eq 1 ] ; then + if systemctl -q is-enabled snapd.socket > /dev/null 2>&1 ; then + systemctl start snapd.socket > /dev/null 2>&1 || : + fi + if systemctl -q is-enabled snapd.refresh.timer > /dev/null 2>&1 ; then + systemctl start snapd.refresh.timer > /dev/null 2>&1 || : + fi +fi + +%preun +%systemd_preun %{snappy_svcs} + +# Remove all Snappy content if snapd is being fully uninstalled +if [ $1 -eq 0 ]; then + %{_libexecdir}/snapd/snap-mgmt --purge || : +fi + + +%postun +%systemd_postun_with_restart %{snappy_svcs} + +%pre selinux +%selinux_relabel_pre + +%post selinux +%selinux_modules_install %{_datadir}/selinux/packages/snappy.pp.bz2 +%selinux_relabel_post + +%postun selinux +%selinux_modules_uninstall snappy +if [ $1 -eq 0 ]; then + %selinux_relabel_post +fi + + +%changelog +* Thu Nov 09 2017 Michael Vogt +- New upstream release 2.29.3 + - daemon: cherry-picked /v2/logs fixes + - cmd/snap-confine: Respect biarch nature of libdirs + - cmd/snap-confine: Ensure snap-confine is allowed to access os- + release + - interfaces: fix udev tagging for hooks + - cmd: fix re-exec bug with classic confinement for host snapd + - tests: disable xdg-open-compat test + - cmd/snap-confine: add slave PTYs and let devpts newinstance + perform mediation + - interfaces/many: misc policy updates for browser-support, cups- + control and network-status + - interfaces/raw-usb: match on SUBSYSTEM, not SUBSYSTEMS + - tests: fix security-device-cgroup* tests on devices with + framebuffer + +* Fri Nov 03 2017 Michael Vogt +- New upstream release 2.29.2 + - snapctl: disable stop/start/restart (2.29) + - cmd/snap-update-ns: fix collection of changes made + +* Fri Nov 03 2017 Michael Vogt +- New upstream release 2.29.1 + - interfaces: fix incorrect signature of ofono DBusPermanentSlot + - interfaces/serial-port: udev tag plugged slots that have just + 'path' via KERNEL + - interfaces/hidraw: udev tag plugged slots that have just 'path' + via KERNEL + - interfaces/uhid: unconditionally add existing uhid device to the + device cgroup + - cmd/snap-update-ns: fix mount rules for font sharing + - tests: disable refresh-undo test on trusty for now + - tests: use `snap change --last=install` in snapd-reexec test + - Revert " wrappers: fail install if exec-line cannot be re-written + - interfaces: don't udev tag devmode or classic snaps + - many: make ignore-validation sticky and send the flag with refresh + requests + +* Mon Oct 30 2017 Michael Vogt +- New upstream release 2.29 + - interfaces/many: miscellaneous updates based on feedback from the + field + - snap-confine: allow reading uevents from any where in /sys + - spread: add bionic beaver + - debian: make packaging/ubuntu-14.04/copyright a real file again + - tests: cherry pick the fix for services test into 2.29 + - cmd/snap-update-ns: initialize logger + - hooks/configure: queue service restarts + - snap-{confine,seccomp}: make @unrestricted fully unrestricted + - interfaces: clean system apparmor cache on core device + - debian: do not build static snap-exec on powerpc + - snap-confine: increase sanity_timeout to 6s + - snapctl: cherry pick service commands changes + - cmd/snap: tell translators about arg names and descs req's + - systemd: run all mount units before snapd.service to avoid race + - store: add a test to show auth failures are forwarded by doRequest + - daemon: convert ErrInvalidCredentials to a 401 Unauthorized error. + - store: forward on INVALID_CREDENTIALS error as + ErrInvalidCredentials + - daemon: generate a forbidden response message if polkit dialog is + dismissed + - daemon: Allow Polkit authorization to cancel changes. + - travis: switch to container based test runs + - interfaces: reduce duplicated code in interface tests mocks + - tests: improve revert related testing + - interfaces: sanitize plugs and slots early in ReadInfo + - store: add download caching + - preserve TMPDIR and HOSTALIASES across snap-confine invocation + - snap-confine: init all arrays with `= {0,}` + - tests: adding test for network-manager interface + - interfaces/mount: don't generate legacy per-hook/per-app mount + profiles + - snap: introduce structured epochs + - tests: fix interfaces-cups-control test for cups-2.2.5 + - snap-confine: cleanup incorrectly created nvidia udev tags + - cmd/snap-confine: update valid security tag regexp + - cmd/libsnap: enable two stranded tests + - cmd,packaging: enable apparmor on openSUSE + - overlord/ifacestate: refresh all security backends on startup + - interfaces/dbus: drop unneeded check for + release.ReleaseInfo.ForceDevMode + - dbus: ensure io.snapcraft.Launcher.service is created on re- + exec + - overlord/auth: continue for now supporting UBUNTU_STORE_ID if the + model is generic-classic + - snap-confine: add support for handling /dev/nvidia-modeset + - interfaces/network-control: remove incorrect rules for tun + - spread: allow setting SPREAD_DEBUG_EACH=0 to disable debug-each + section + - packaging: remove .mnt files on removal + - tests: fix econnreset scenario when the iptables rule was not + created + - tests: add test for lxd interface + - run-checks: use nakedret static checker to check for naked + returns on long functions + - progress: be more flexible in testing ansimeter + - interfaces: fix udev rules for tun + - many: implement our own ANSI-escape-using progress indicator + - snap-exec: update tests to follow main_test pattern + - snap: support "command: foo $ENV_STRING" + - packaging: update nvidia configure options + - snap: add new `snap pack` and use in tests + - cmd: correctly name the "Ubuntu" and "Arch" NVIDIA methods + - cmd: add autogen case for solus + - tests: do not use http://canihazip.com/ which appears to be down + - hooks: commands for controlling own services from snapctl + - snap: refactor cmdGet.Execute() + - interfaces/mount: make Change.Perform testable and test it + - interfaces/mount,cmd/snap-update-ns: move change code + - snap-confine: is_running_on_classic_distribution() looks into os- + release + - interfaces: misc updates for default, browser-support, home and + system-observe + - interfaces: deny lttng by default + - interfaces/lxd: lxd slot implementation can also be an app snap + - release,cmd,dirs: Redo the distro checks to take into account + distribution families + - cmd/snap: completion for alias and unalias + - snap-confine: add new SC_CLEANUP and use it + - snap: refrain from running filepath.Base on random strings + - cmd/snap-confine: put processes into freezer hierarchy + - wrappers: fail install if exec-line cannot be re-written + - cmd/snap-seccomp,osutil: make user/group lookup functions public + - snapstate: deal with snap user data in the /root/ directory + - interfaces: Enhance full-confinement support for biarch + distributions + - snap-confine: Only attempt to copy/mount NVIDIA libs when NVIDIA + is used + - packaging/fedora: Add Fedora 26, 27, and Rawhide symlinks + - overlord/snapstate: prefer a smaller corner case for doing the + wrong thing + - cmd/snap-repair: set user agent for snap-repair http requests + - packaging: bring down the delta between 14.04 and 16.04 + - snap-confine: Ensure lib64 biarch directory is respected + - snap-confine: update apparmor rules for fedora based base snaps + - tests: Increase SNAPD_CONFIGURE_HOOK_TIMEOUT to 3 minutes to + install real snaps + - daemon: use client.Snap instead of map[string]interface{} for + snaps. + - hooks: rename refresh hook to post-refresh + - git: make the .gitingore file a bit more targeted + - interfaces/opengl: don't udev tag nvidia devices and use snap- + confine instead + - cmd/snap-{confine,update-ns}: apply mount profiles using snap- + update-ns + - cmd: update "make hack" + - interfaces/system-observe: allow clients to enumerate DBus + connection names + - snap-repair: implement `snap-repair {list,show}` + - dirs,interfaces: create snap-confine.d on demand when re-executing + - snap-confine: fix base snaps on core + - cmd/snap-repair: fix tests when running as root + - interfaces: add Connection type + - cmd/snap-repair: skip disabled repairs + - cmd/snap-repair: prefer leaking unmanaged fds on test failure over + closing random ones + - snap-repair: make `repair` binary available for repair scripts + - snap-repair: fix missing Close() in TestStatusHappy + - cmd/snap-confine,packaging: import snapd-generated policy + - cmd/snap: return empty document if snap has no configuration + - snap-seccomp: run secondary-arch tests via gcc-multilib + - snap: implement `snap {repair,repairs}` and pass-through to snap- + repair + - interfaces/builtin: allow receiving dbus messages + - snap-repair: implement `snap-repair {done,skip,retry}` + - data/completion: small tweak to snap completion snippet + - dirs: fix classic support detection + - cmd/snap-repair: integrate root public keys for repairs + - tests: fix ubuntu core services + - tests: add new test that checks that the compat snapd-xdg-open + works + - snap-confine: improve error message if core/u-core cannot be found + - tests: only run tests/regression/nmcli on amd64 + - interfaces: mount host system fonts in desktop interface + - interfaces: enable partial apparmor support + - snapstate: auto-install missing base snaps + - spread: work around temporary packaging issue in debian sid + - asserts,cmd/snap-repair: introduce a mandatory summary for repairs + - asserts,cmd/snap-repair: represent RepairID internally as an int + - tests: test the real "xdg-open" from the core snap + - many: implement fetching sections and package names periodically. + - interfaces/network: allow using netcat as client + - snap-seccomp, osutil: use osutil.AtomicFile in snap-seccomp + - snap-seccomp: skip mknod syscall on arm64 + - tests: add trivial canonical-livepatch test + - tests: add test that ensures that all core services are working + - many: add logger.MockLogger() and use it in the tests + - snap-repair: fix test failure in TestRepairHitsTimeout + - asserts: add empty values check in HeadersFromPrimaryKey + - daemon: remove unused installSnap var in test + - daemon: reach for Overlord.Loop less thanks to overlord.Mock + - snap-seccomp: manually resolve socket() call in tests + - tests: change regex used to validate installed ubuntu core snap + - cmd/snapctl: allow snapctl -h without a context (regression fix). + - many: use snapcore/snapd/i18n instead of i18n/dumb + - many: introduce asserts.NotFoundError replacing both ErrNotFound + and store.AssertionNotFoundError + - packaging: don't include any marcos in comments + - overlord: use overlord.Mock in more tests, make sure we check the + outcome of Settle + - tests: try to fix staging tests + - store: simplify api base url config + - systemd: add systemd.MockJournalctl() + - many: provide systemd.MockSystemctl() helper + - tests: improve the listing test to not fail for e.g. 2.28~rc2 + - snapstate: give snapmgrTestSuite.settle() more time to settle + - tests: fix regex to check core version on snap list + - debian: update trusted account-keys check on 14.04 packaging + - interfaces: add udev netlink support to hardware-observe + - overlord: introduce Mock which enables to use Overlord.Settle for + settle in many more places + - snap-repair: execute the repair and capture logs/status + - tests: run the tests/unit/go everywhere + - daemon, snapstate: move ensureCore from daemon/api.go into + snapstate.go + - cmd/snap: get keys or root document + - spread.yaml: turn suse to manual given that it's breaking master + - many: configure store from state, reconfigure store at runtime + - osutil: AtomicWriter (an io.Writer), and io.Reader versions of + AtomicWrite* + - tests: check for negative syscalls in runBpf() and skip those + tests + - docs: use abolute path in PULL_REQUEST_TEMPLATE.md + - store: move device auth endpoint uris to config (#3831) + +* Fri Oct 13 2017 Michael Vogt +- New upstream release 2.28.5 + - snap-confine: cleanup broken nvidia udev tags + - cmd/snap-confine: update valid security tag regexp + - overlord/ifacestate: refresh udev backend on startup + - dbus: ensure io.snapcraft.Launcher.service is created on re- + exec + - snap-confine: add support for handling /dev/nvidia-modeset + - interfaces/network-control: remove incorrect rules for tun + +* Wed Oct 11 2017 Michael Vogt +- New upstream release 2.28.4 + - interfaces/opengl: don't udev tag nvidia devices and use snap- + confine instead + - debian: fix replaces/breaks for snap-xdg-open (thanks to apw!) + +* Wed Oct 11 2017 Michael Vogt +- New upstream release 2.28.3 + - interfaces/lxd: lxd slot implementation can also be an app + snap + +* Tue Oct 10 2017 Michael Vogt +- New upstream release 2.28.2 + - interfaces: fix udev rules for tun + - release,cmd,dirs: Redo the distro checks to take into account + distribution families + +* Wed Sep 27 2017 Michael Vogt +- New upstream release 2.28.1 + - snap-confine: update apparmor rules for fedora based basesnaps + - snapstate: rename refresh hook to post-refresh for consistency + +* Mon Sep 25 2017 Michael Vogt +- New upstream release 2.28 + - hooks: rename refresh to after-refresh + - snap-confine: bind mount /usr/lib/snapd relative to snap-confine + - cmd,dirs: treat "liri" the same way as "arch" + - snap-confine: fix base snaps on core + - hooks: substitute env vars when executing hooks + - interfaces: updates for default, browser-support, desktop, opengl, + upower and stub-resolv.conf + - cmd,dirs: treat manjaro the same as arch + - systemd: do not run auto-import and repair services on classic + - packaging/fedora: Ensure vendor/ is empty for builds and fix spec + to build current master + - many: fix TestSetConfNumber missing an Unlock and other fragility + improvements + - osutil: adjust StreamCommand tests for golang 1.9 + - daemon: allow polkit authorisation to install/remove snaps + - tests: make TestCmdWatch more robust + - debian: improve package description + - interfaces: add netlink kobject uevent to hardware observe + - debian: update trusted account-keys check on 14.04 packaging + - interfaces/network-{control,observe}: allow receiving + kobject_uevent() messages + - tests: fix lxd test for external backend + - snap-confine,snap-update-ns: add -no-pie to fix FTBFS on + go1.7,ppc64 + - corecfg: mock "systemctl" in all corecfg tests + - tests: fix unit tests on Ubuntu 14.04 + - debian: add missing flags when building static snap-exec + - many: end-to-end support for the bare base snap + - overlord/snapstate: SetRootDir from SetUpTest, not in just some + tests + - store: have an ad-hoc method on cfg to get its list of uris for + tests + - daemon: let client decide whether to allow interactive auth via + polkit + - client,daemon,snap,store: add license field + - overlord/snapstate: rename HasCurrent to IsInstalled, remove + superfluous/misleading check from All + - cmd/snap: SetRootDir from SetUpTest, not in just some individual + tests. + - systemd: rename snap-repair.{service,timer} to snapd.snap- + repair.{service,timer} + - snap-seccomp: remove use of x/net/bpf from tests + - httputil: more naive per go version way to recreate a default + transport for tls reconfig + - cmd/snap-seccomp/main_test.go: add one more syscall for arm64 + - interfaces/opengl: use == to compare, not = + - cmd/snap-seccomp/main_test.go: add syscalls for armhf and arm64 + - cmd/snap-repair: track and use a lower bound for the time for + TLS checks + - interfaces: expose bluez interface on classic OS + - snap-seccomp: add in-kernel bpf tests + - overlord: always try to get a serial, lazily on classic + - tests: add nmcli regression test + - tests: deal with __PNR_chown on aarch64 to fix FTBFS on arm64 + - tests: add autopilot-introspection interface test + - vendor: fix artifact from manually editing vendor/vendor.json + - tests: rename complexion to test-snapd-complexion + - interfaces: add desktop and desktop-legacy + interfaces/desktop: add new 'desktop' interface for modern DEs* + interfaces/builtin/desktop_test.go: use modern testing techniques* + interfaces/wayland: allow read on /etc/drirc for Plasma desktop* + interfaces/desktop-legacy: add new 'legacy' interface (currently + for a11y and input) + - tests: fix race in snap userd test + - devices/iio: add read/write for missing sysfs entries + - spread: don't set HTTPS?_PROXY for linode + - cmd/snap-repair: check signatures of repairs from Next + - env: set XDG_DATA_DIRS for wayland et.al. + - interfaces/{default,account-control}: Use username/group instead + of uid/gid + - interfaces/builtin: use udev tagging more broadly + - tests: add basic lxd test + - wrappers: ensure bash completion snaps install on core + - vendor: use old golang.org/x/crypto/ssh/terminal to build on + powerpc again + - docs: add PULL_REQUEST_TEMPLATE.md + - interfaces: fix network-manager plug + - hooks: do not error out when hook is optional and no hook handler + is registered + - cmd/snap: add userd command to replace snapd-xdg-open + - tests: new regex used to validate the core version on extra snaps + ass... + - snap: add new `snap switch` command + - tests: wait more and more debug info about fakestore start issues + - apparmor,release: add better apparmor detection/mocking code + - interfaces/i2c: adjust sysfs rule for alternate paths + - interfaces/apparmor: add missing call to dirs.SetRootDir + - cmd: "make hack" now also installs snap-update-ns + - tests: copy files with less verbosity + - cmd/snap-confine: allow using additional libraries required by + openSUSE + - packaging/fedora: Merge changes from Fedora Dist-Git + - snapstate: improve the error message when classic confinement is + not supported + - tests: add test to ensure amd64 can run i386 syscall binaries + - tests: adding extra info for fakestore when fails to start + - tests: install most important snaps + - cmd/snap-repair: more test coverage of filtering + - squashfs: remove runCommand/runCommandWithOutput as we do not need + it + - cmd/snap-repair: ignore superseded revisions, filter on arch and + models + - hooks: support for refresh hook + - Partial revert "overlord/devicestate, store: update device auth + endpoints URLs" + - cmd/snap-confine: allow reading /proc/filesystems + - cmd/snap-confine: genearlize apparmor profile for various lib + layout + - corecfg: fix proxy.* writing and add integration test + - corecfg: deal with system.power-key-action="" correctly + - vendor: update vendor.json after (presumed) manual edits + - cmd/snap: in `snap info`, don't print a newline between tracks + - daemon: add polkit support to /v2/login + - snapd,snapctl: decode json using Number + - client: fix go vet 1.7 errors + - tests: make 17.04 shellcheck clean + - tests: remove TestInterfacesHelp as it breaks when go-flags + changes + - snapstate: undo a daemon restart on classic if needed + - cmd/snap-repair: recover brand/model from + /var/lib/snapd/seed/assertions checking signatures and brand + account + - spread: opt into unsafe IO during spread tests + - snap-repair: update snap-repair/runner_test.go for API change in + makeMockServer + - cmd/snap-repair: skeleton code around actually running a repair + - tests: wait until the port is listening after start the fake store + - corecfg: fix typo in tests + - cmd/snap-repair: test that redirects works during fetching + - osutil: honor SNAPD_UNSAFE_IO for testing + - vendor: explode and make more precise our golang.go/x/crypto deps, + use same version as Debian unstable + - many: sanitize NewStoreStack signature, have shared default store + test private keys + - systemd: disable `Nice=-5` to fix error when running inside lxd + - spread.yaml: update delta ref to 2.27 + - cmd/snap-repair: use E-Tags when refetching a repair to retry + - interfaces/many: updates based on chromium and mrrescue denials + - cmd/snap-repair: implement most logic to get the next repair to + run/retry in a brand sequence + - asserts/assertstest: copy headers in SigningDB.Sign + - interfaces: convert uhid to common interface and test cases + improvement for time_control and opengl + - many tests: move all panicing fake store methods to a common place + - asserts: add store assertion type + - interfaces: don't crash if content slot has no attributes + - debian: do not build with -buildmode=pie on i386 + - wrappers: symlink completion snippets when symlinking binaries + - tests: adding more debug information for the interfaces-cups- + control … + - apparmor: pass --quiet to parser on load unless SNAPD_DEBUG is set + - many: allow and support serials signed by the 'generic' authority + instead of the brand + - corecfg: add proxy configuration via `snap set core + proxy.{http,https,ftp}=...` + - interfaces: a bunch of interfaces test improvement + - tests: enable regression and completion suites for opensuse + - tests: installing snapd for nested test suite + - interfaces: convert lxd_support to common iface + - interfaces: add missing test for camera interface. + - snap: add support for parsing snap layout section + - cmd/snap-repair: like for downloads we cannot have a timeout (at + least for now), less aggressive retry strategies + - overlord: rely on more conservative ensure interval + - overlord,store: no piles of return args for methods gathering + device session request params + - overlord,store: send model assertion when setting up device + sessions + - interfaces/misc: updates for unity7/x11, browser- + support, network-control and mount-observe + interfaces/unity7,x11: update for NETLINK_KOBJECT_UEVENT + interfaces/browser-support: update sysfs reads for + newer browser versions, interfaces/network-control: rw for + ieee80211 advanced wireless interfaces/mount-observe: allow read + on sysfs entries for block devices + - tests: use dnf --refresh install to avert stale cache + - osutil: ensure TestLockUnlockWorks uses supported flock + - interfaces: convert lxd to common iface + - tests: restart snapd to ensure re-exec settings are applied + - tests: fix interfaces-cups-control test + - interfaces: improve and tweak bunch of interfaces test cases. + - tests: adding extra worker for fedora + - asserts,overlord/devicestate: support predefined assertions that + don't establish foundational trust + - interfaces: convert two hardware_random interfaces to common iface + - interfaces: convert io_ports_control to common iface + - tests: fix for upgrade test on fedora + - daemon, client, cmd/snap: implement snap start/stop/restart + - cmd/snap-confine: set _FILE_OFFSET_BITS to 64 + - interfaces: covert framebuffer to commonInterface + - interfaces: convert joystick to common iface + - interfaces/builtin: add the spi interface + - wrappers, overlord/snapstate/backend: make link-snap clean up on + failure. + - interfaces/wayland: add wayland interface + - interfaces: convert kvm to common iface + - tests: extend upower-observe test to cover snaps providing slots + - tests: enable main suite for opensuse + - interfaces: convert physical_memory_observe to common iface + - interfaces: add missing test for optical_drive interface. + - interfaces: convert physical_memory_control to common iface + - interfaces: convert ppp to common iface + - interfaces: convert time-control to common iface + - tests: fix failover test + - interfaces/builtin: rework for avahi interface + - interfaces: convert broadcom-asic-control to common iface + - snap/snapenv: document the use of CoreSnapMountDir for SNAP + - packaging/arch: drop patches merged into master + - cmd: fix mustUnsetenv docstring (thanks to Chipaca) + - release: remove default from VERSION_ID + - tests: enable regression, upgrade and completion test suites for + fedora + - tests: restore interfaces-account-control properly + - overlord/devicestate, store: update device auth endpoints URLs + - tests: fix install-hook test failure + - tests: download core and ubuntu-core at most once + - interfaces: add common support for udev + - overlord/devicestate: fix, don't assume that the serial is backed + by a 1-key chain + - cmd/snap-confine: don't share /etc/nsswitch from host + - store: do not resume a download when we already have the whole + thing + - many: implement "snap logs" + - store: don't call useDeltas() twice in quick succession + - interfaces/builtin: add kvm interface + - snap/snapenv: always expect /snap for $SNAP + - cmd: mark arch as non-reexecing distro + - cmd: fix tests that assume /snap mount + - gitignore: ignore more build artefacts + - packaging: add current arch packaging + - interfaces/unity7: allow receiving media key events in (at least) + gnome-shell + - interfaces/many, cmd/snap-confine: miscellaneous policy updates + - interfaces/builtin: implement broadcom-asic-control interface + - interfaces/builtin: reduce duplication and remove cruft in + Sanitize{Plug,Slot} + - tests: apply underscore convention for SNAPMOUNTDIR variable + - interfaces/greengrass-support: adjust accesses now that have + working snap + - daemon, client, cmd/snap: implement "snap services" + - tests: fix refresh tests not stopping fake store for fedora + - many: add the interface command + - overlord/snapstate/backend: some copydata improvements + - many: support querying and completing assertion type names + - interfaces/builtin: discard empty Validate{Plug,Slot} + - cmd/snap-repair: start of Runner, implement first pass of Peek + and Fetch + - tests: enable main suite on fedora + - snap: do not always quote the snap info summary + - vendor: update go-flags to address crash in "snap debug" + - interfaces: opengl support pci device and vendor + - many: start implenting "base" snap type on the snapd side + - arch,release: map armv6 correctly + - many: expose service status in 'snap info' + - tests: add browser-support interface test + - tests: disable snapd-notify for the external backend + - interfaces: Add /run/uuid/request to openvswitch + - interfaces: add password-manager-service implicit classic + interface + - cmd: rework reexec detection + - cmd: fix re-exec bug when starting from snapd 2.21 + - tests: dependency packages installed during prepare-project + - tests: remove unneeded check for re-exec in InternalToolPath() + - cmd,tests: fix classic confinement confusing re-execution code + - store: configurable base api + - tests: fix how package lists are updated for opensuse and fedora + +* Thu Sep 07 2017 Michael Vogt +- New upstream release 2.27.6 + - interfaces: add udev netlink support to hardware-observe + - interfaces/network-{control,observe}: allow receiving + kobject_uevent() messages + +* Wed Aug 30 2017 Michael Vogt +- New upstream release 2.27.5 + - interfaces: fix network-manager plug regression + - hooks: do not error when hook handler is not registered + - interfaces/alsa,pulseaudio: allow read on udev data for sound + - interfaces/optical-drive: read access to udev data for /dev/scd* + - interfaces/browser-support: read on /proc/vmstat and misc udev + data + +* Thu Aug 24 2017 Michael Vogt +- New upstream release 2.27.4 + - snap-seccomp: add secondary arch for unrestricted snaps as well + +* Fri Aug 18 2017 Michael Vogt +- New upstream release 2.27.3 + - systemd: disable `Nice=-5` to fix error when running inside lxdSee + https://bugs.launchpad.net/snapd/+bug/1709536 + +* Wed Aug 16 2017 Neal Gompa - 2.27.2-2 +- Bump to rebuild for F27 and Rawhide + +* Wed Aug 16 2017 Neal Gompa - 2.27.2-1 +- Release 2.27.2 to Fedora (RH#1482173) + +* Wed Aug 16 2017 Michael Vogt +- New upstream release 2.27.2 + - tests: remove TestInterfacesHelp as it breaks when go-flags + changes + - interfaces: don't crash if content slot has no attributes + - debian: do not build with -buildmode=pie on i386 + - interfaces: backport broadcom-asic-control interface + - interfaces: allow /usr/bin/xdg-open in unity7 + - store: do not resume a download when we already have the whole + thing + +* Mon Aug 14 2017 Neal Gompa - 2.27.1-1 +- Release 2.27.1 to Fedora (RH#1481247) + +* Mon Aug 14 2017 Michael Vogt +- New upstream release 2.27.1 + - tests: use dnf --refresh install to avert stale cache + - tests: fix test failure on 14.04 due to old version of + flock + - updates for unity7/x11, browser-support, network-control, + mount-observe + - interfaces/unity7,x11: update for NETLINK_KOBJECT_UEVENT + - interfaces/browser-support: update sysfs reads for + newer browser versions + - interfaces/network-control: rw for ieee80211 advanced wireless + - interfaces/mount-observe: allow read on sysfs entries for block + devices + +* Thu Aug 10 2017 Neal Gompa - 2.27-1 +- Release 2.27 to Fedora (RH#1458086) + +* Thu Aug 10 2017 Michael Vogt +- New upstream release 2.27 + - fix build failure on 32bit fedora + - interfaces: add password-manager-service implicit classic interface + - interfaces/greengrass-support: adjust accesses now that have working + snap + - interfaces/many, cmd/snap-confine: miscellaneous policy updates + - interfaces/unity7: allow receiving media key events in (at least) + gnome-shell + - cmd: fix re-exec bug when starting from snapd 2.21 + - tests: restore interfaces-account-control properly + - cmd: fix tests that assume /snap mount + - cmd: mark arch as non-reexecing distro + - snap-confine: don't share /etc/nsswitch from host + - store: talk to api.snapcraft.io for purchases + - hooks: support for install and remove hooks + - packaging: fix Fedora support + - tests: add bluetooth-control interface test + - store: talk to api.snapcraft.io for assertions + - tests: remove snapd before building from branch + - tests: add avahi-observe interface test + - store: orders API now checks if customer is ready + - cmd/snap: snap find only searches stable + - interfaces: updates default, mir, optical-observe, system-observe, + screen-inhibit-control and unity7 + - tests: speedup prepare statement part 1 + - store: do not send empty refresh requests + - asserts: fix error handling in snap-developer consistency check + - systemd: add explicit sync to snapd.core-fixup.sh + - snapd: generate snap cookies on startup + - cmd,client,daemon: expose "force devmode" in sysinfo + - many: introduce and use strutil.ListContains and also + strutil.SortedListContains + - assserts,overlord/assertstate: test we don't accept chains of + assertions founded on a self-signed key coming externally + - interfaces: enable access to bridge settings + - interfaces: fix copy-pasted iio vs io in io-ports-control + - cmd/snap-confine: various small fixes and tweaks to seccomp + support code + - interfaces: bring back seccomp argument filtering + - systemd, osutil: rework systemd logs in preparation for services + commands + - tests: store /etc/systemd/system/snap-*core*.mount in snapd- + state.tar.gz + - tests: shellcheck improvements for tests/main tasks - first set of + tests + - cmd/snap: `--last` for abort and watch, and aliases + (search→find, change→tasks) + - tests: shellcheck improvements for tests/lib scripts + - tests: create ramdisk if it's not present + - tests: shellcheck improvements for nightly upgrade and regressions + tests + - snapd: fix for snapctl get panic on null config values. + - tests: fix for rng-tools service not restarting + - systemd: add snapd.core-fixup.service unit + - cmd: avoid using current symlink in InternalToolPath + - tests: fix timeout issue for test refresh core with hanging … + - intefaces: control bridged vlan/ppoe-tagged traffic + - cmd/snap: include snap type in notes + - overlord/state: Abort() only visits each task once + - tests: extend find-private test to cover more cases + - snap-seccomp: skip socket() tests on systems that use socketcall() + instead of socket() + - many: support snap title as localized/title-cased name + - snap-seccomp: deal with mknod on aarch64 in the seccomp tests + - interfaces: put base policy fragments inside each interface + - asserts: introduce NewDecoderWithTypeMaxBodySize + - tests: fix snapd-notify when it takes more time to restart + - snap-seccomp: fix snap-seccomp tests in artful + - tests: fix for create-key task to avoid rng-tools service ramains + alive + - snap-seccomp: make sure snap-seccomp writes the bpf file + atomically + - tests: do not disable ipv6 on core systems + - arch: the kernel architecture name is armv7l instead of armv7 + - snap-confine: ensure snap-confine waits some seconds for seccomp + security profiles + - tests: shellcheck improvements for tests/nested tasks + - wrappers: add SyslogIdentifier to the service unit files. + - tests: shellcheck improvements for unit tasks + - asserts: implement FindManyTrusted as well + - asserts: open up and optimize Encoder to help avoiding unnecessary + copying + - interfaces: simplify snap-confine by just loading pre-generated + bpf code + - tests: restart rng-tools services after few seconds + - interfaces, tests: add mising dbus abstraction to system-observe + and extend spread test + - store: change main store host to api.snapcraft.io + - overlord/cmdstate: new package for running commands as tasks. + - spread: help libapt resolve installing libudev-dev + - tests: show the IP from .travis.yaml + - tests/main: use pkgdb function in more test cases + - cmd,daemon: add debug command for displaying the base policy + - tests: prevent quoting error on opensuse + - tests: fix nightly suite + - tests: add linode-sru backend + - snap-confine: validate SNAP_NAME against security tag + - tests: fix ipv6 disable for ubuntu-core + - tests: extend core-revert test to cover bluez issues + - interfaces/greengrass-support: add support for Amazon Greengrass + as a snap + - asserts: support timestamp and optional disabled header on repair + - tests: reboot after upgrading to snapd on the -proposed pocket + - many: fix test cases to work with different DistroLibExecDir + - tests: reenable help test on ubuntu and debian systems + - packaging/{opensuse,fedora}: allow package build with testkeys + included + - tests/lib: generalize RPM build support + - interfaces/builtin: sync connected slot and permanent slot snippet + - tests: fix snap create-key by restarting automatically rng-tools + - many: switch to use http numeric statuses as agreed + - debian: add missing Type=notify in 14.04 packaging + - tests: mark interfaces-openvswitch as manual due to prepare errors + - debian: unify built_using between the 14.04 and 16.04 packaging + branch + - tests: pull from urandom when real entropy is not enough + - tests/main/manpages: install missing man package + - tests: add refresh --time output check + - debian: add missing "make -C data/systemd clean" + - tests: fix for upgrade test when it is repeated + - tests/main: use dir abstraction in a few more test cases + - tests/main: check for confinement in a few more interface tests + - spread: add fedora snap bin dir to global PATH + - tests: check that locale-control is not present on core + - many: snapctl outside hooks + - tests: add whoami check + - interfaces: compose the base declaration from interfaces + - tests: fix spread flaky tests linode + - tests,packaging: add package build support for openSUSE + - many: slight improvement of some snap error messaging + - errtracker: Include /etc/apparmor.d/usr.lib.snap-confine md5sum in + err reports + - tests: fix for the test postrm-purge + - tests: restoring the /etc/environment and service units config for + each test + - daemon: make snapd a "Type=notify" daemon and notify when startup + is done + - cmd/snap-confine: add support for --base snap + - many: derive implicit slots from interface meta-data + - tests: add core revert test + - tests,packaging: add package build support for Fedora for our + spread setup + - interfaces: move base declaration to the policy sub-package + - tests: fix for snapd-reexec test cheking for restart info on debug + log + - tests: show available entropy on error + - tests: clean journalctl logs on trusty + - tests: fix econnreset on staging + - tests: modify core before calling set + - tests: add snap-confine privilege test + - tests: add staging snap-id + - interfaces/builtin: silence ptrace denial for network-manager + - tests: add alsa interface spread test + - tests: prefer ipv4 over ipv6 + - tests: fix for econnreset test checking that the download already + started + - httputil,store: extract retry code to httputil, reorg usages + - errtracker: report if snapd did re-execute itself + - errtracker: include bits of snap-confine apparmor profile + - tests: take into account staging snap-ids for snap-info + - cmd: add stub new snap-repair command and add timer + - many: stop "snap refresh $x --channel invalid" from working + - interfaces: revert "interfaces: re-add reverted ioctl and quotactl + - snapstate: consider connect/disconnect tasks in + CheckChangeConflict. + - interfaces: disable "mknod |N" in the default seccomp template + again + - interfaces,overlord/ifacestate: make sure installing slots after + plugs works similarly to plugs after slots + - interfaces/seccomp: add bind() syscall for forced-devmode systems + - packaging/fedora: Sync packaging from Fedora Dist-Git + - tests: move static and unit tests to spread task + - many: error types should be called FooError, not ErrFoo. + - partition: add directory sync to the save uboot.env file code + - cmd: test everything (100% coverage \o/) + - many: make shell scripts shellcheck-clean + - tests: remove additional setup for docker on core + - interfaces: add summary to each interface + - many: remove interface meta-data from list of connections + - logger (& many more, to accommodate): drop explicit syslog. + - packaging: import packaging bits for opensuse + - snapstate,many: implement snap install --unaliased + - tests/lib: abstract build dependency installation a bit more + - interfaces, osutil: move flock code from interfaces/mount to + osutil + - cmd: auto import assertions only from ext4,vfat file systems + - many: refactor in preparation for 'snap start' + - overlord/snapstate: have an explicit code path last-refresh + unset/zero => immediately refresh try + - tests: fixes for executions using the staging store + - tests: use pollinate to seed the rng + - cmd/snap,tests: show the sha3-384 of the snap for snap info + --verbose SNAP-FILE + - asserts: simplify and adjust repair assertion definition + - cmd/snap,tests: show the snap id if available in snap info + - daemon,overlord/auth: store from model assertion wins + - cmd/snap,tests/main: add confinement switch instead of spread + system blacklisting + - many: cleanup MockCommands and don't leave a process around after + hookstate tests + - tests: update listing test to the core version number schema + - interfaces: allow snaps to use the timedatectl utility + - packaging: Add Fedora packaging files + - tests/libs: add distro_auto_remove_packages function + - cmd/snap: correct devmode note for anomalous state + - tests/main/snap-info: use proper pkgdb functions to install distro + packages + - tests/lib: use mktemp instead of tempfile to work cross-distro + - tests: abstract common dirs which differ on distributions + - many: model and expose interface meta-data. + - overlord: make config defaults from gadget work also at first boot + - interfaces/log-observe: allow using journalctl from hostfs for + classic distro + - partition,snap: add support for android boot + - errtracker: small simplification around readMachineID + - snap-confine: move rm_rf_tmp to test-utils. + - tests/lib: introduce pkgdb helper library + - errtracker: try multiple paths to read machine-id + - overlord/hooks: make sure only one hook for given snap is executed + at a time. + - cmd/snap-confine: use SNAP_MOUNT_DIR to setup /snap inside the + confinement env + - tests: bump kill-timeout and remove quiet call on build + - tests/lib/snaps: add a test store snap with a passthrough + configure hook + - daemon: teach the daemon to wait on active connections when + shutting down + - tests: remove unit tests task + - tests/main/completion: source from /usr/share/bash-completion + - assertions: add "repair" assertion + - interfaces/seccomp: document Backend.NewSpecification + - wrappers: make StartSnapServices cleanup any services that were + added if a later one fails + - overlord/snapstate: avoid creating command aliases for daemons + - vendor: remove unused packages + - vendor,partition: fix panics from uenv + - cmd,interfaces/mount: run snap-update-ns and snap-discard-ns from + core if possible + - daemon: do not allow to install ubuntu-core anymore + - wrappers: service start/stop were inconsistent + - tests: fix failing tests (snap core version, syslog changes) + - cmd/snap-update-ns: add actual implementation + - tests: improve entropy also for ubuntu + - cmd/snap-confine: use /etc/ssl from the core snap + - wrappers: don't convert between []byte and string needlessly. + - hooks: default timeout + - overlord/snapstate: Enable() was ignoring the flags from the + snap's state, resulting in losing "devmode" on disable/enable. + - difs,interfaces/mount: add support for locking namespaces + - interfaces/mount: keep track of kept mount entries + - tests/main: move a bunch of greps over to MATCH + - interfaces/builtin: make all interfaces private + - interfaces/mount: spell unmount correctly + - tests: allow 16-X.Y.Z version of core snap + - the timezone_control interface only allows changing /etc/timezone + and /etc/writable/timezone. systemd-timedated also updated the + link of /etc/localtime and /etc/writable/localtime ... allow + access to this file too + - cmd/snap-confine: aggregate operations holding global lock + - api, ifacestate: resolve disconnect early + - interfaces/builtin: ensure we don't register interfaces twice + +* Thu Aug 03 2017 Fedora Release Engineering - 2.26.3-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 2.26.3-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Thu May 25 2017 Neal Gompa - 2.26.3-3 +- Cover even more stuff for proper erasure on final uninstall (RH#1444422) + +* Sun May 21 2017 Neal Gompa - 2.26.3-2 +- Fix error in script for removing Snappy content (RH#1444422) +- Adjust changelog bug references to be specific on origin + +* Wed May 17 2017 Neal Gompa - 2.26.3-1 +- Update to snapd 2.26.3 +- Drop merged and unused patches +- Cover more Snappy content for proper erasure on final uninstall (RH#1444422) +- Add temporary fix to ensure generated seccomp profiles don't break snapctl + +* Mon May 01 2017 Neal Gompa - 2.25-1 +- Update to snapd 2.25 +- Ensure all Snappy content is gone on final uninstall (RH#1444422) + +* Tue Apr 11 2017 Neal Gompa - 2.24-1 +- Update to snapd 2.24 +- Drop merged patches +- Install snap bash completion and snapd info file + +* Wed Apr 05 2017 Neal Gompa - 2.23.6-4 +- Test if snapd socket and timer enabled and start them if enabled on install + +* Sat Apr 01 2017 Neal Gompa - 2.23.6-3 +- Fix profile.d generation so that vars aren't expanded in package build + +* Fri Mar 31 2017 Neal Gompa - 2.23.6-2 +- Fix the overlapping file conflicts between snapd and snap-confine +- Rework package descriptions slightly + +* Thu Mar 30 2017 Neal Gompa - 2.23.6-1 +- Rebase to snapd 2.23.6 +- Rediff patches +- Re-enable seccomp +- Fix building snap-confine on 32-bit arches +- Set ExclusiveArch based on upstream supported arch list + +* Wed Mar 29 2017 Neal Gompa - 2.23.5-1 +- Rebase to snapd 2.23.5 +- Disable seccomp temporarily avoid snap-confine bugs (LP#1674193) +- Use vendorized build for non-Fedora + +* Mon Mar 13 2017 Neal Gompa - 2.23.1-1 +- Rebase to snapd 2.23.1 +- Add support for vendored tarball for non-Fedora targets +- Use merged in SELinux policy module + +* Sat Feb 11 2017 Fedora Release Engineering - 2.16-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Wed Oct 19 2016 Zygmunt Krynicki - 2.16-1 +- New upstream release + +* Tue Oct 18 2016 Neal Gompa - 2.14-2 +- Add SELinux policy module subpackage + +* Tue Aug 30 2016 Zygmunt Krynicki - 2.14-1 +- New upstream release + +* Tue Aug 23 2016 Zygmunt Krynicki - 2.13-1 +- New upstream release + +* Thu Aug 18 2016 Zygmunt Krynicki - 2.12-2 +- Correct license identifier + +* Thu Aug 18 2016 Zygmunt Krynicki - 2.12-1 +- New upstream release + +* Thu Aug 18 2016 Zygmunt Krynicki - 2.11-8 +- Add %%dir entries for various snapd directories +- Tweak Source0 URL + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-7 +- Disable snapd re-exec feature by default + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-6 +- Don't auto-start snapd.socket and snapd.refresh.timer + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-5 +- Don't touch snapd state on removal + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-4 +- Use ExecStartPre to load squashfs.ko before snapd starts +- Use dedicated systemd units for Fedora + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-3 +- Remove systemd preset (will be requested separately according to distribution + standards). + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-2 +- Use Requires: kmod(squashfs.ko) instead of Requires: kernel-modules + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-1 +- New upstream release +- Move private executables to /usr/libexec/snapd/ + +* Fri Jun 24 2016 Zygmunt Krynicki - 2.0.9-2 +- Depend on kernel-modules to ensure that squashfs can be loaded. Load it afer + installing the package. This hopefully fixes + https://github.com/zyga/snapcore-fedora/issues/2 + +* Fri Jun 17 2016 Zygmunt Krynicki - 2.0.9 +- New upstream release + https://github.com/snapcore/snapd/releases/tag/2.0.9 + +* Tue Jun 14 2016 Zygmunt Krynicki - 2.0.8.1 +- New upstream release + +* Fri Jun 10 2016 Zygmunt Krynicki - 2.0.8 +- First package for Fedora diff -Nru snapd-2.28.5/packaging/fedora-26/snap-mgmt.sh snapd-2.29.3/packaging/fedora-26/snap-mgmt.sh --- snapd-2.28.5/packaging/fedora-26/snap-mgmt.sh 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/packaging/fedora-26/snap-mgmt.sh 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,137 @@ +#!/bin/bash + +# Overlord management of snapd for package manager actions. +# Implements actions that would be invoked in %pre(un) actions for snapd. +# Derived from the snapd.postrm scriptlet used in the Ubuntu packaging for +# snapd. + +set -e + +SNAP_MOUNT_DIR="/var/lib/snapd/snap" + +show_help() { + exec cat <<'EOF' +Usage: snap-mgmt.sh [OPTIONS] + +A simple script to cleanup snap installations. + +optional arguments: + --help Show this help message and exit + --snap-mount-dir= Provide a path to be used as $SNAP_MOUNT_DIR + --purge Purge all data from $SNAP_MOUNT_DIR +EOF +} + +SNAP_UNIT_PREFIX="$(systemd-escape -p ${SNAP_MOUNT_DIR})" + +systemctl_stop() { + unit="$1" + if systemctl is-active -q "$unit"; then + echo "Stoping $unit" + systemctl stop -q "$unit" || true + fi +} + +purge() { + # undo any bind mount to ${SNAP_MOUNT_DIR} that resulted from LP:#1668659 + if grep -q "${SNAP_MOUNT_DIR} ${SNAP_MOUNT_DIR}" /proc/self/mountinfo; then + umount -l "${SNAP_MOUNT_DIR}" || true + fi + + mounts=$(systemctl list-unit-files --full | grep "^${SNAP_UNIT_PREFIX}[-.].*\.mount" | cut -f1 -d ' ') + services=$(systemctl list-unit-files --full | grep "^${SNAP_UNIT_PREFIX}[-.].*\.service" | cut -f1 -d ' ') + for unit in $services $mounts; do + # ensure its really a snap mount unit or systemd unit + if ! grep -q 'What=/var/lib/snapd/snaps/' "/etc/systemd/system/$unit" && ! grep -q 'X-Snappy=yes' "/etc/systemd/system/$unit"; then + echo "Skipping non-snapd systemd unit $unit" + continue + fi + + echo "Stopping $unit" + systemctl_stop "$unit" + + # if it is a mount unit, we can find the snap name in the mount + # unit (we just ignore unit files) + snap=$(grep "Where=${SNAP_MOUNT_DIR}/" "/etc/systemd/system/$unit"|cut -f3 -d/) + rev=$(grep "Where=${SNAP_MOUNT_DIR}/" "/etc/systemd/system/$unit"|cut -f4 -d/) + if [ -n "$snap" ]; then + echo "Removing snap $snap" + # aliases + if [ -d "${SNAP_MOUNT_DIR}/bin" ]; then + find "${SNAP_MOUNT_DIR}/bin" -maxdepth 1 -lname "$snap" -delete + find "${SNAP_MOUNT_DIR}/bin" -maxdepth 1 -lname "$snap.*" -delete + fi + # generated binaries + rm -f "${SNAP_MOUNT_DIR}/bin/$snap" + rm -f "${SNAP_MOUNT_DIR}/bin/$snap".* + # snap mount dir + umount -l "${SNAP_MOUNT_DIR}/$snap/$rev" 2> /dev/null || true + rm -rf "${SNAP_MOUNT_DIR:?}/$snap/$rev" + rm -f "${SNAP_MOUNT_DIR}/$snap/current" + # snap data dir + rm -rf "/var/snap/$snap/$rev" + rm -rf "/var/snap/$snap/common" + rm -f "/var/snap/$snap/current" + # opportunistic remove (may fail if there are still revisions left) + for d in "${SNAP_MOUNT_DIR}/$snap" "/var/snap/$snap"; do + if [ -d "$d" ]; then + rmdir --ignore-fail-on-non-empty "$d" + fi + done + fi + + echo "Removing $unit" + rm -f "/etc/systemd/system/$unit" + rm -f "/etc/systemd/system/multi-user.target.wants/$unit" + done + + echo "Discarding preserved snap namespaces" + # opportunistic as those might not be actually mounted + for mnt in /run/snapd/ns/*.mnt; do + umount -l "$mnt" || true + rm -f "$mnt" + done + for fstab in /run/snapd/ns/*.fstab; do + rm -f "$fstab" + done + umount -l /run/snapd/ns/ || true + + + echo "Removing downloaded snaps" + rm -rf /var/lib/snapd/snaps/* + + echo "Final directory cleanup" + rm -rf "${SNAP_MOUNT_DIR}" + rm -rf /var/snap + + echo "Removing leftover snap shared state data" + rm -rf /var/lib/snapd/desktop/applications/* + rm -rf /var/lib/snapd/seccomp/bpf/* + rm -rf /var/lib/snapd/device/* + rm -rf /var/lib/snapd/assertions/* + + echo "Removing snapd catalog cache" + rm -f /var/cache/snapd/* +} + +while [ -n "$1" ]; do + case "$1" in + --help) + show_help + exit + ;; + --snap-mount-dir=*) + SNAP_MOUNT_DIR=${1#*=} + SNAP_UNIT_PREFIX=$(systemd-escape -p "$SNAP_MOUNT_DIR") + shift + ;; + --purge) + purge + shift + ;; + *) + echo "Unknown command: $1" + exit 1 + ;; + esac +done diff -Nru snapd-2.28.5/packaging/fedora-27/snapd.spec snapd-2.29.3/packaging/fedora-27/snapd.spec --- snapd-2.28.5/packaging/fedora-27/snapd.spec 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/packaging/fedora-27/snapd.spec 2017-11-09 18:16:16.000000000 +0000 @@ -0,0 +1,1630 @@ +# With Fedora, nothing is bundled. For everything else, bundling is used. +# To use bundled stuff, use "--with vendorized" on rpmbuild +%if 0%{?fedora} +%bcond_with vendorized +%else +%bcond_without vendorized +%endif + +# A switch to allow building the package with support for testkeys which +# are used for the spread test suite of snapd. +%bcond_with testkeys + +%global with_devel 1 +%global with_debug 1 +%global with_check 0 +%global with_unit_test 0 +%global with_test_keys 0 + +# For the moment, we don't support all golang arches... +%global with_goarches 0 + +%if ! %{with vendorized} +%global with_bundled 0 +%else +%global with_bundled 1 +%endif + +%if ! %{with testkeys} +%global with_test_keys 0 +%else +%global with_test_keys 1 +%endif + +%if 0%{?with_debug} +%global _dwz_low_mem_die_limit 0 +%else +%global debug_package %{nil} +%endif + +%global provider github +%global provider_tld com +%global project snapcore +%global repo snapd +# https://github.com/snapcore/snapd +%global provider_prefix %{provider}.%{provider_tld}/%{project}/%{repo} +%global import_path %{provider_prefix} + +%global snappy_svcs snapd.service snapd.socket snapd.autoimport.service snapd.refresh.timer snapd.refresh.service + +Name: snapd +Version: 2.29.3 +Release: 0%{?dist} +Summary: A transactional software package manager +Group: System Environment/Base +License: GPLv3 +URL: https://%{provider_prefix} +%if ! 0%{?with_bundled} +Source0: https://%{provider_prefix}/archive/%{version}/%{name}-%{version}.tar.gz +%else +Source0: https://%{provider_prefix}/releases/download/%{version}/%{name}_%{version}.vendor.orig.tar.xz +%endif + +%if 0%{?with_goarches} +# e.g. el6 has ppc64 arch without gcc-go, so EA tag is required +ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}} +%else +# Verified arches from snapd upstream +ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 ppc64le s390x +%endif + +# If go_compiler is not set to 1, there is no virtual provide. Use golang instead. +BuildRequires: %{?go_compiler:compiler(go-compiler)}%{!?go_compiler:golang} +BuildRequires: systemd +%{?systemd_requires} + +Requires: snap-confine%{?_isa} = %{version}-%{release} +Requires: squashfs-tools +# we need squashfs.ko loaded +Requires: kmod(squashfs.ko) +# bash-completion owns /usr/share/bash-completion/completions +Requires: bash-completion + +# Force the SELinux module to be installed +Requires: %{name}-selinux = %{version}-%{release} + +%if ! 0%{?with_bundled} +BuildRequires: golang(github.com/cheggaaa/pb) +BuildRequires: golang(github.com/coreos/go-systemd/activation) +BuildRequires: golang(github.com/godbus/dbus) +BuildRequires: golang(github.com/godbus/dbus/introspect) +BuildRequires: golang(github.com/gorilla/mux) +BuildRequires: golang(github.com/jessevdk/go-flags) +BuildRequires: golang(github.com/mvo5/uboot-go/uenv) +BuildRequires: golang(github.com/ojii/gettext.go) +BuildRequires: golang(github.com/seccomp/libseccomp-golang) +BuildRequires: golang(golang.org/x/crypto/openpgp/armor) +BuildRequires: golang(golang.org/x/crypto/openpgp/packet) +BuildRequires: golang(golang.org/x/crypto/sha3) +BuildRequires: golang(golang.org/x/crypto/ssh/terminal) +BuildRequires: golang(golang.org/x/net/context) +BuildRequires: golang(golang.org/x/net/context/ctxhttp) +BuildRequires: golang(gopkg.in/check.v1) +BuildRequires: golang(gopkg.in/macaroon.v1) +BuildRequires: golang(gopkg.in/mgo.v2/bson) +BuildRequires: golang(gopkg.in/retry.v1) +BuildRequires: golang(gopkg.in/tomb.v2) +BuildRequires: golang(gopkg.in/yaml.v2) +%endif + +%description +Snappy is a modern, cross-distribution, transactional package manager +designed for working with self-contained, immutable packages. + +%package -n snap-confine +Summary: Confinement system for snap applications +License: GPLv3 +Group: System Environment/Base +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: libtool +BuildRequires: gcc +BuildRequires: gettext +BuildRequires: gnupg +BuildRequires: indent +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(libcap) +BuildRequires: pkgconfig(libseccomp) +BuildRequires: pkgconfig(libudev) +BuildRequires: pkgconfig(systemd) +BuildRequires: pkgconfig(udev) +BuildRequires: xfsprogs-devel +BuildRequires: glibc-static +BuildRequires: libseccomp-static +BuildRequires: valgrind +BuildRequires: %{_bindir}/rst2man +%if 0%{?fedora} >= 25 +# ShellCheck in F24 and older doesn't work +BuildRequires: %{_bindir}/shellcheck +%endif + +# Ensures older version from split packaging is replaced +Obsoletes: snap-confine < 2.19 + +%description -n snap-confine +This package is used internally by snapd to apply confinement to +the started snap applications. + +%package selinux +Summary: SELinux module for snapd +Group: System Environment/Base +License: GPLv2+ +BuildArch: noarch +BuildRequires: selinux-policy, selinux-policy-devel +Requires(post): selinux-policy-base >= %{_selinux_policy_version} +Requires(post): policycoreutils +Requires(post): policycoreutils-python-utils +Requires(pre): libselinux-utils +Requires(post): libselinux-utils + +%description selinux +This package provides the SELinux policy module to ensure snapd +runs properly under an environment with SELinux enabled. + + +%if 0%{?with_devel} +%package devel +Summary: %{summary} +BuildArch: noarch + +%if 0%{?with_check} && ! 0%{?with_bundled} +%endif + +%if ! 0%{?with_bundled} +Requires: golang(github.com/cheggaaa/pb) +Requires: golang(github.com/coreos/go-systemd/activation) +Requires: golang(github.com/godbus/dbus) +Requires: golang(github.com/godbus/dbus/introspect) +Requires: golang(github.com/gorilla/mux) +Requires: golang(github.com/jessevdk/go-flags) +Requires: golang(github.com/mvo5/uboot-go/uenv) +Requires: golang(github.com/ojii/gettext.go) +Requires: golang(github.com/seccomp/libseccomp-golang) +Requires: golang(golang.org/x/crypto/openpgp/armor) +Requires: golang(golang.org/x/crypto/openpgp/packet) +Requires: golang(golang.org/x/crypto/sha3) +Requires: golang(golang.org/x/crypto/ssh/terminal) +Requires: golang(golang.org/x/net/context) +Requires: golang(golang.org/x/net/context/ctxhttp) +Requires: golang(gopkg.in/check.v1) +Requires: golang(gopkg.in/macaroon.v1) +Requires: golang(gopkg.in/mgo.v2/bson) +Requires: golang(gopkg.in/retry.v1) +Requires: golang(gopkg.in/tomb.v2) +Requires: golang(gopkg.in/yaml.v2) +%else +# These Provides are unversioned because the sources in +# the bundled tarball are unversioned (they go by git commit) +# *sigh*... I hate golang... +Provides: bundled(golang(github.com/cheggaaa/pb)) +Provides: bundled(golang(github.com/coreos/go-systemd/activation)) +Provides: bundled(golang(github.com/godbus/dbus)) +Provides: bundled(golang(github.com/godbus/dbus/introspect)) +Provides: bundled(golang(github.com/gorilla/mux)) +Provides: bundled(golang(github.com/jessevdk/go-flags)) +Provides: bundled(golang(github.com/mvo5/uboot-go/uenv)) +Provides: bundled(golang(github.com/mvo5/libseccomp-golang)) +Provides: bundled(golang(github.com/ojii/gettext.go)) +Provides: bundled(golang(golang.org/x/crypto/openpgp/armor)) +Provides: bundled(golang(golang.org/x/crypto/openpgp/packet)) +Provides: bundled(golang(golang.org/x/crypto/sha3)) +Provides: bundled(golang(golang.org/x/crypto/ssh/terminal)) +Provides: bundled(golang(golang.org/x/net/context)) +Provides: bundled(golang(golang.org/x/net/context/ctxhttp)) +Provides: bundled(golang(gopkg.in/check.v1)) +Provides: bundled(golang(gopkg.in/macaroon.v1)) +Provides: bundled(golang(gopkg.in/mgo.v2/bson)) +Provides: bundled(golang(gopkg.in/retry.v1)) +Provides: bundled(golang(gopkg.in/tomb.v2)) +Provides: bundled(golang(gopkg.in/yaml.v2)) +%endif + +# Generated by gofed +Provides: golang(%{import_path}/arch) = %{version}-%{release} +Provides: golang(%{import_path}/asserts) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/assertstest) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/signtool) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/snapasserts) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/sysdb) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/systestkeys) = %{version}-%{release} +Provides: golang(%{import_path}/boot) = %{version}-%{release} +Provides: golang(%{import_path}/boot/boottest) = %{version}-%{release} +Provides: golang(%{import_path}/client) = %{version}-%{release} +Provides: golang(%{import_path}/cmd) = %{version}-%{release} +Provides: golang(%{import_path}/daemon) = %{version}-%{release} +Provides: golang(%{import_path}/dirs) = %{version}-%{release} +Provides: golang(%{import_path}/errtracker) = %{version}-%{release} +Provides: golang(%{import_path}/httputil) = %{version}-%{release} +Provides: golang(%{import_path}/i18n) = %{version}-%{release} +Provides: golang(%{import_path}/image) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/apparmor) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/backends) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/builtin) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/dbus) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/ifacetest) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/kmod) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/mount) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/policy) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/seccomp) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/systemd) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/udev) = %{version}-%{release} +Provides: golang(%{import_path}/logger) = %{version}-%{release} +Provides: golang(%{import_path}/osutil) = %{version}-%{release} +Provides: golang(%{import_path}/overlord) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/assertstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/auth) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/cmdstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/configstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/configstate/config) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/devicestate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/hookstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/hookstate/ctlcmd) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/hookstate/hooktest) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/ifacestate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/patch) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/snapstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/snapstate/backend) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/state) = %{version}-%{release} +Provides: golang(%{import_path}/partition) = %{version}-%{release} +Provides: golang(%{import_path}/partition/androidbootenv) = %{version}-%{release} +Provides: golang(%{import_path}/partition/grubenv) = %{version}-%{release} +Provides: golang(%{import_path}/partition/ubootenv) = %{version}-%{release} +Provides: golang(%{import_path}/progress) = %{version}-%{release} +Provides: golang(%{import_path}/release) = %{version}-%{release} +Provides: golang(%{import_path}/snap) = %{version}-%{release} +Provides: golang(%{import_path}/snap/snapdir) = %{version}-%{release} +Provides: golang(%{import_path}/snap/snapenv) = %{version}-%{release} +Provides: golang(%{import_path}/snap/snaptest) = %{version}-%{release} +Provides: golang(%{import_path}/snap/squashfs) = %{version}-%{release} +Provides: golang(%{import_path}/store) = %{version}-%{release} +Provides: golang(%{import_path}/strutil) = %{version}-%{release} +Provides: golang(%{import_path}/systemd) = %{version}-%{release} +Provides: golang(%{import_path}/tests/lib/fakestore/refresh) = %{version}-%{release} +Provides: golang(%{import_path}/tests/lib/fakestore/store) = %{version}-%{release} +Provides: golang(%{import_path}/testutil) = %{version}-%{release} +Provides: golang(%{import_path}/timeout) = %{version}-%{release} +Provides: golang(%{import_path}/timeutil) = %{version}-%{release} +Provides: golang(%{import_path}/wrappers) = %{version}-%{release} +Provides: golang(%{import_path}/x11) = %{version}-%{release} + + +%description devel +%{summary} + +This package contains library source intended for +building other packages which use import path with +%{import_path} prefix. +%endif + +%if 0%{?with_unit_test} && 0%{?with_devel} +%package unit-test-devel +Summary: Unit tests for %{name} package + +%if 0%{?with_check} +#Here comes all BuildRequires: PACKAGE the unit tests +#in %%check section need for running +%endif + +%if 0%{?with_check} && ! 0%{?with_bundled} +BuildRequires: golang(github.com/mvo5/goconfigparser) +%endif + +%if ! 0%{?with_bundled} +Requires: golang(github.com/mvo5/goconfigparser) +%else +Provides: bundled(golang(github.com/mvo5/goconfigparser)) +%endif + +# test subpackage tests code from devel subpackage +Requires: %{name}-devel = %{version}-%{release} + +%description unit-test-devel +%{summary} + +This package contains unit tests for project +providing packages with %{import_path} prefix. +%endif + +%prep +%setup -q + +%if ! 0%{?with_bundled} +# Ensure there's no bundled stuff accidentally leaking in... +rm -rf vendor/* + +# XXX: HACK: Fake that we have the right import path because bad testing +# did not verify that this path was actually valid on all supported systems. +mkdir -p vendor/gopkg.in/cheggaaa +ln -s %{gopath}/src/github.com/cheggaaa/pb vendor/gopkg.in/cheggaaa/pb.v1 + +%endif + +%build +# Generate version files +./mkversion.sh "%{version}-%{release}" + +# Build snapd +mkdir -p src/github.com/snapcore +ln -s ../../../ src/github.com/snapcore/snapd + +%if ! 0%{?with_bundled} +export GOPATH=$(pwd):%{gopath} +%else +export GOPATH=$(pwd):$(pwd)/Godeps/_workspace:%{gopath} +%endif + +GOFLAGS= +%if 0%{?with_test_keys} +GOFLAGS="$GOFLAGS -tags withtestkeys" +%endif + +# We have to build snapd first to prevent the build from +# building various things from the tree without additional +# set tags. +%gobuild -o bin/snapd $GOFLAGS %{import_path}/cmd/snapd +%gobuild -o bin/snap $GOFLAGS %{import_path}/cmd/snap +%gobuild -o bin/snapctl $GOFLAGS %{import_path}/cmd/snapctl +# build snap-exec and snap-update-ns completely static for base snaps +CGO_ENABLED=0 %gobuild -o bin/snap-exec $GOFLAGS %{import_path}/cmd/snap-exec +%gobuild -o bin/snap-update-ns --ldflags '-extldflags "-static"' $GOFLAGS %{import_path}/cmd/snap-update-ns + +# We don't need mvo5 fork for seccomp, as we have seccomp 2.3.x +sed -e "s:github.com/mvo5/libseccomp-golang:github.com/seccomp/libseccomp-golang:g" -i cmd/snap-seccomp/*.go +%gobuild -o bin/snap-seccomp $GOFLAGS %{import_path}/cmd/snap-seccomp + +# Build SELinux module +pushd ./data/selinux +make SHARE="%{_datadir}" TARGETS="snappy" +popd + +# Build snap-confine +pushd ./cmd +# FIXME This is a hack to get rid of a patch we have to ship for the +# Fedora package at the moment as /usr/lib/rpm/redhat/redhat-hardened-ld +# accidentially adds -pie for static executables. See +# https://bugzilla.redhat.com/show_bug.cgi?id=1343892 for a few more +# details. To prevent this from happening we drop the linker +# script and define our LDFLAGS manually for now. +export LDFLAGS="-Wl,-z,relro -z now" +autoreconf --force --install --verbose +# selinux support is not yet available, for now just disable apparmor +# FIXME: add --enable-caps-over-setuid as soon as possible (setuid discouraged!) +%configure \ + --disable-apparmor \ + --libexecdir=%{_libexecdir}/snapd/ \ + --with-snap-mount-dir=%{_sharedstatedir}/snapd/snap \ + --with-merged-usr + +%make_build +popd + +# Build systemd units +pushd ./data/ +make BINDIR="%{_bindir}" LIBEXECDIR="%{_libexecdir}" \ + SYSTEMDSYSTEMUNITDIR="%{_unitdir}" \ + SNAP_MOUNT_DIR="%{_sharedstatedir}/snapd/snap" \ + SNAPD_ENVIRONMENT_FILE="%{_sysconfdir}/sysconfig/snapd" +popd + +# Build environ-tweaking snippet +make -C data/env SNAP_MOUNT_DIR="%{_sharedstatedir}/snapd/snap" + +%install +install -d -p %{buildroot}%{_bindir} +install -d -p %{buildroot}%{_libexecdir}/snapd +install -d -p %{buildroot}%{_mandir}/man1 +install -d -p %{buildroot}%{_unitdir} +install -d -p %{buildroot}%{_sysconfdir}/profile.d +install -d -p %{buildroot}%{_sysconfdir}/sysconfig +install -d -p %{buildroot}%{_sharedstatedir}/snapd/assertions +install -d -p %{buildroot}%{_sharedstatedir}/snapd/desktop/applications +install -d -p %{buildroot}%{_sharedstatedir}/snapd/device +install -d -p %{buildroot}%{_sharedstatedir}/snapd/hostfs +install -d -p %{buildroot}%{_sharedstatedir}/snapd/mount +install -d -p %{buildroot}%{_sharedstatedir}/snapd/seccomp/bpf +install -d -p %{buildroot}%{_sharedstatedir}/snapd/snaps +install -d -p %{buildroot}%{_sharedstatedir}/snapd/snap/bin +install -d -p %{buildroot}%{_localstatedir}/snap +install -d -p %{buildroot}%{_localstatedir}/cache/snapd +install -d -p %{buildroot}%{_datadir}/selinux/devel/include/contrib +install -d -p %{buildroot}%{_datadir}/selinux/packages + +# Install snap and snapd +install -p -m 0755 bin/snap %{buildroot}%{_bindir} +install -p -m 0755 bin/snap-exec %{buildroot}%{_libexecdir}/snapd +install -p -m 0755 bin/snapctl %{buildroot}%{_bindir}/snapctl +install -p -m 0755 bin/snapd %{buildroot}%{_libexecdir}/snapd +install -p -m 0755 bin/snap-update-ns %{buildroot}%{_libexecdir}/snapd +install -p -m 0755 bin/snap-seccomp %{buildroot}%{_libexecdir}/snapd + +# Install SELinux module +install -p -m 0644 data/selinux/snappy.if %{buildroot}%{_datadir}/selinux/devel/include/contrib +install -p -m 0644 data/selinux/snappy.pp.bz2 %{buildroot}%{_datadir}/selinux/packages + +# Install snap(1) man page +bin/snap help --man > %{buildroot}%{_mandir}/man1/snap.1 + +# Install the "info" data file with snapd version +install -m 644 -D data/info %{buildroot}%{_libexecdir}/snapd/info + +# Install bash completion for "snap" +install -m 644 -D data/completion/snap %{buildroot}%{_datadir}/bash-completion/completions/snap +install -m 644 -D data/completion/complete.sh %{buildroot}%{_libexecdir}/snapd +install -m 644 -D data/completion/etelpmoc.sh %{buildroot}%{_libexecdir}/snapd + +# Install snap-confine +pushd ./cmd +%make_install +# Undo the 0000 permissions, they are restored in the files section +chmod 0755 %{buildroot}%{_sharedstatedir}/snapd/void +# We don't use AppArmor +rm -rfv %{buildroot}%{_sysconfdir}/apparmor.d +# ubuntu-core-launcher is dead +rm -fv %{buildroot}%{_bindir}/ubuntu-core-launcher +popd + +# Install all systemd units +pushd ./data/ +%make_install SYSTEMDSYSTEMUNITDIR="%{_unitdir}" BINDIR="%{_bindir}" LIBEXECDIR="%{_libexecdir}" +# Remove snappy core specific units +rm -fv %{buildroot}%{_unitdir}/snapd.system-shutdown.service +rm -fv %{buildroot}%{_unitdir}/snapd.snap-repair.* +rm -fv %{buildroot}%{_unitdir}/snapd.core-fixup.* +popd + +# Remove snappy core specific scripts +rm %{buildroot}%{_libexecdir}/snapd/snapd.core-fixup.sh + +# Install environ-tweaking snippet +pushd ./data/env +%make_install +popd + +# Disable re-exec by default +echo 'SNAP_REEXEC=0' > %{buildroot}%{_sysconfdir}/sysconfig/snapd + +# Install snap management script +install -pm 0755 packaging/fedora/snap-mgmt.sh %{buildroot}%{_libexecdir}/snapd/snap-mgmt + +# Create state.json file to be ghosted +touch %{buildroot}%{_sharedstatedir}/snapd/state.json + +# source codes for building projects +%if 0%{?with_devel} +install -d -p %{buildroot}/%{gopath}/src/%{import_path}/ +echo "%%dir %%{gopath}/src/%%{import_path}/." >> devel.file-list +# find all *.go but no *_test.go files and generate devel.file-list +for file in $(find . -iname "*.go" -o -iname "*.s" \! -iname "*_test.go") ; do + echo "%%dir %%{gopath}/src/%%{import_path}/$(dirname $file)" >> devel.file-list + install -d -p %{buildroot}/%{gopath}/src/%{import_path}/$(dirname $file) + cp -pav $file %{buildroot}/%{gopath}/src/%{import_path}/$file + echo "%%{gopath}/src/%%{import_path}/$file" >> devel.file-list +done +%endif + +# testing files for this project +%if 0%{?with_unit_test} && 0%{?with_devel} +install -d -p %{buildroot}/%{gopath}/src/%{import_path}/ +# find all *_test.go files and generate unit-test.file-list +for file in $(find . -iname "*_test.go"); do + echo "%%dir %%{gopath}/src/%%{import_path}/$(dirname $file)" >> devel.file-list + install -d -p %{buildroot}/%{gopath}/src/%{import_path}/$(dirname $file) + cp -pav $file %{buildroot}/%{gopath}/src/%{import_path}/$file + echo "%%{gopath}/src/%%{import_path}/$file" >> unit-test-devel.file-list +done + +# Install additional testdata +install -d %{buildroot}/%{gopath}/src/%{import_path}/cmd/snap/test-data/ +cp -pav cmd/snap/test-data/* %{buildroot}/%{gopath}/src/%{import_path}/cmd/snap/test-data/ +echo "%%{gopath}/src/%%{import_path}/cmd/snap/test-data" >> unit-test-devel.file-list +%endif + +%if 0%{?with_devel} +sort -u -o devel.file-list devel.file-list +%endif + +%check +# snapd tests +%if 0%{?with_check} && 0%{?with_unit_test} && 0%{?with_devel} +%if ! 0%{?with_bundled} +export GOPATH=%{buildroot}/%{gopath}:%{gopath} +%else +export GOPATH=%{buildroot}/%{gopath}:$(pwd)/Godeps/_workspace:%{gopath} +%endif +%gotest %{import_path}/... +%endif + +# snap-confine tests (these always run!) +pushd ./cmd +make check +popd + +%files +#define license tag if not already defined +%{!?_licensedir:%global license %doc} +%license COPYING +%doc README.md docs/* +%{_bindir}/snap +%{_bindir}/snapctl +%dir %{_libexecdir}/snapd +%{_libexecdir}/snapd/snapd +%{_libexecdir}/snapd/snap-exec +%{_libexecdir}/snapd/info +%{_libexecdir}/snapd/snap-mgmt +%{_mandir}/man1/snap.1* +%{_datadir}/bash-completion/completions/snap +%{_libexecdir}/snapd/complete.sh +%{_libexecdir}/snapd/etelpmoc.sh +%{_sysconfdir}/profile.d/snapd.sh +%{_unitdir}/snapd.socket +%{_unitdir}/snapd.service +%{_unitdir}/snapd.autoimport.service +%{_unitdir}/snapd.refresh.service +%{_unitdir}/snapd.refresh.timer +%config(noreplace) %{_sysconfdir}/sysconfig/snapd +%dir %{_sharedstatedir}/snapd +%dir %{_sharedstatedir}/snapd/assertions +%dir %{_sharedstatedir}/snapd/desktop +%dir %{_sharedstatedir}/snapd/desktop/applications +%dir %{_sharedstatedir}/snapd/device +%dir %{_sharedstatedir}/snapd/hostfs +%dir %{_sharedstatedir}/snapd/mount +%dir %{_sharedstatedir}/snapd/seccomp +%dir %{_sharedstatedir}/snapd/seccomp/bpf +%dir %{_sharedstatedir}/snapd/snaps +%dir %{_sharedstatedir}/snapd/snap +%dir /var/cache/snapd +%ghost %dir %{_sharedstatedir}/snapd/snap/bin +%dir %{_localstatedir}/snap +%ghost %{_sharedstatedir}/snapd/state.json +%{_datadir}/dbus-1/services/io.snapcraft.Launcher.service + +%files -n snap-confine +%doc cmd/snap-confine/PORTING +%license COPYING +%dir %{_libexecdir}/snapd +# For now, we can't use caps +# FIXME: Switch to "%%attr(0755,root,root) %%caps(cap_sys_admin=pe)" asap! +%attr(4755,root,root) %{_libexecdir}/snapd/snap-confine +%{_libexecdir}/snapd/snap-discard-ns +%{_libexecdir}/snapd/snap-seccomp +%{_libexecdir}/snapd/snap-update-ns +%{_libexecdir}/snapd/system-shutdown +%{_mandir}/man1/snap-confine.1* +%{_mandir}/man5/snap-discard-ns.5* +%{_prefix}/lib/udev/snappy-app-dev +%{_udevrulesdir}/80-snappy-assign.rules +%attr(0000,root,root) %{_sharedstatedir}/snapd/void + + +%files selinux +%license data/selinux/COPYING +%doc data/selinux/README.md +%{_datadir}/selinux/packages/snappy.pp.bz2 +%{_datadir}/selinux/devel/include/contrib/snappy.if + +%if 0%{?with_devel} +%files devel -f devel.file-list +%license COPYING +%doc README.md +%dir %{gopath}/src/%{provider}.%{provider_tld}/%{project} +%endif + +%if 0%{?with_unit_test} && 0%{?with_devel} +%files unit-test-devel -f unit-test-devel.file-list +%license COPYING +%doc README.md +%endif + +%post +%systemd_post %{snappy_svcs} +# If install, test if snapd socket and timer are enabled. +# If enabled, then attempt to start them. This will silently fail +# in chroots or other environments where services aren't expected +# to be started. +if [ $1 -eq 1 ] ; then + if systemctl -q is-enabled snapd.socket > /dev/null 2>&1 ; then + systemctl start snapd.socket > /dev/null 2>&1 || : + fi + if systemctl -q is-enabled snapd.refresh.timer > /dev/null 2>&1 ; then + systemctl start snapd.refresh.timer > /dev/null 2>&1 || : + fi +fi + +%preun +%systemd_preun %{snappy_svcs} + +# Remove all Snappy content if snapd is being fully uninstalled +if [ $1 -eq 0 ]; then + %{_libexecdir}/snapd/snap-mgmt --purge || : +fi + + +%postun +%systemd_postun_with_restart %{snappy_svcs} + +%pre selinux +%selinux_relabel_pre + +%post selinux +%selinux_modules_install %{_datadir}/selinux/packages/snappy.pp.bz2 +%selinux_relabel_post + +%postun selinux +%selinux_modules_uninstall snappy +if [ $1 -eq 0 ]; then + %selinux_relabel_post +fi + + +%changelog +* Thu Nov 09 2017 Michael Vogt +- New upstream release 2.29.3 + - daemon: cherry-picked /v2/logs fixes + - cmd/snap-confine: Respect biarch nature of libdirs + - cmd/snap-confine: Ensure snap-confine is allowed to access os- + release + - interfaces: fix udev tagging for hooks + - cmd: fix re-exec bug with classic confinement for host snapd + - tests: disable xdg-open-compat test + - cmd/snap-confine: add slave PTYs and let devpts newinstance + perform mediation + - interfaces/many: misc policy updates for browser-support, cups- + control and network-status + - interfaces/raw-usb: match on SUBSYSTEM, not SUBSYSTEMS + - tests: fix security-device-cgroup* tests on devices with + framebuffer + +* Fri Nov 03 2017 Michael Vogt +- New upstream release 2.29.2 + - snapctl: disable stop/start/restart (2.29) + - cmd/snap-update-ns: fix collection of changes made + +* Fri Nov 03 2017 Michael Vogt +- New upstream release 2.29.1 + - interfaces: fix incorrect signature of ofono DBusPermanentSlot + - interfaces/serial-port: udev tag plugged slots that have just + 'path' via KERNEL + - interfaces/hidraw: udev tag plugged slots that have just 'path' + via KERNEL + - interfaces/uhid: unconditionally add existing uhid device to the + device cgroup + - cmd/snap-update-ns: fix mount rules for font sharing + - tests: disable refresh-undo test on trusty for now + - tests: use `snap change --last=install` in snapd-reexec test + - Revert " wrappers: fail install if exec-line cannot be re-written + - interfaces: don't udev tag devmode or classic snaps + - many: make ignore-validation sticky and send the flag with refresh + requests + +* Mon Oct 30 2017 Michael Vogt +- New upstream release 2.29 + - interfaces/many: miscellaneous updates based on feedback from the + field + - snap-confine: allow reading uevents from any where in /sys + - spread: add bionic beaver + - debian: make packaging/ubuntu-14.04/copyright a real file again + - tests: cherry pick the fix for services test into 2.29 + - cmd/snap-update-ns: initialize logger + - hooks/configure: queue service restarts + - snap-{confine,seccomp}: make @unrestricted fully unrestricted + - interfaces: clean system apparmor cache on core device + - debian: do not build static snap-exec on powerpc + - snap-confine: increase sanity_timeout to 6s + - snapctl: cherry pick service commands changes + - cmd/snap: tell translators about arg names and descs req's + - systemd: run all mount units before snapd.service to avoid race + - store: add a test to show auth failures are forwarded by doRequest + - daemon: convert ErrInvalidCredentials to a 401 Unauthorized error. + - store: forward on INVALID_CREDENTIALS error as + ErrInvalidCredentials + - daemon: generate a forbidden response message if polkit dialog is + dismissed + - daemon: Allow Polkit authorization to cancel changes. + - travis: switch to container based test runs + - interfaces: reduce duplicated code in interface tests mocks + - tests: improve revert related testing + - interfaces: sanitize plugs and slots early in ReadInfo + - store: add download caching + - preserve TMPDIR and HOSTALIASES across snap-confine invocation + - snap-confine: init all arrays with `= {0,}` + - tests: adding test for network-manager interface + - interfaces/mount: don't generate legacy per-hook/per-app mount + profiles + - snap: introduce structured epochs + - tests: fix interfaces-cups-control test for cups-2.2.5 + - snap-confine: cleanup incorrectly created nvidia udev tags + - cmd/snap-confine: update valid security tag regexp + - cmd/libsnap: enable two stranded tests + - cmd,packaging: enable apparmor on openSUSE + - overlord/ifacestate: refresh all security backends on startup + - interfaces/dbus: drop unneeded check for + release.ReleaseInfo.ForceDevMode + - dbus: ensure io.snapcraft.Launcher.service is created on re- + exec + - overlord/auth: continue for now supporting UBUNTU_STORE_ID if the + model is generic-classic + - snap-confine: add support for handling /dev/nvidia-modeset + - interfaces/network-control: remove incorrect rules for tun + - spread: allow setting SPREAD_DEBUG_EACH=0 to disable debug-each + section + - packaging: remove .mnt files on removal + - tests: fix econnreset scenario when the iptables rule was not + created + - tests: add test for lxd interface + - run-checks: use nakedret static checker to check for naked + returns on long functions + - progress: be more flexible in testing ansimeter + - interfaces: fix udev rules for tun + - many: implement our own ANSI-escape-using progress indicator + - snap-exec: update tests to follow main_test pattern + - snap: support "command: foo $ENV_STRING" + - packaging: update nvidia configure options + - snap: add new `snap pack` and use in tests + - cmd: correctly name the "Ubuntu" and "Arch" NVIDIA methods + - cmd: add autogen case for solus + - tests: do not use http://canihazip.com/ which appears to be down + - hooks: commands for controlling own services from snapctl + - snap: refactor cmdGet.Execute() + - interfaces/mount: make Change.Perform testable and test it + - interfaces/mount,cmd/snap-update-ns: move change code + - snap-confine: is_running_on_classic_distribution() looks into os- + release + - interfaces: misc updates for default, browser-support, home and + system-observe + - interfaces: deny lttng by default + - interfaces/lxd: lxd slot implementation can also be an app snap + - release,cmd,dirs: Redo the distro checks to take into account + distribution families + - cmd/snap: completion for alias and unalias + - snap-confine: add new SC_CLEANUP and use it + - snap: refrain from running filepath.Base on random strings + - cmd/snap-confine: put processes into freezer hierarchy + - wrappers: fail install if exec-line cannot be re-written + - cmd/snap-seccomp,osutil: make user/group lookup functions public + - snapstate: deal with snap user data in the /root/ directory + - interfaces: Enhance full-confinement support for biarch + distributions + - snap-confine: Only attempt to copy/mount NVIDIA libs when NVIDIA + is used + - packaging/fedora: Add Fedora 26, 27, and Rawhide symlinks + - overlord/snapstate: prefer a smaller corner case for doing the + wrong thing + - cmd/snap-repair: set user agent for snap-repair http requests + - packaging: bring down the delta between 14.04 and 16.04 + - snap-confine: Ensure lib64 biarch directory is respected + - snap-confine: update apparmor rules for fedora based base snaps + - tests: Increase SNAPD_CONFIGURE_HOOK_TIMEOUT to 3 minutes to + install real snaps + - daemon: use client.Snap instead of map[string]interface{} for + snaps. + - hooks: rename refresh hook to post-refresh + - git: make the .gitingore file a bit more targeted + - interfaces/opengl: don't udev tag nvidia devices and use snap- + confine instead + - cmd/snap-{confine,update-ns}: apply mount profiles using snap- + update-ns + - cmd: update "make hack" + - interfaces/system-observe: allow clients to enumerate DBus + connection names + - snap-repair: implement `snap-repair {list,show}` + - dirs,interfaces: create snap-confine.d on demand when re-executing + - snap-confine: fix base snaps on core + - cmd/snap-repair: fix tests when running as root + - interfaces: add Connection type + - cmd/snap-repair: skip disabled repairs + - cmd/snap-repair: prefer leaking unmanaged fds on test failure over + closing random ones + - snap-repair: make `repair` binary available for repair scripts + - snap-repair: fix missing Close() in TestStatusHappy + - cmd/snap-confine,packaging: import snapd-generated policy + - cmd/snap: return empty document if snap has no configuration + - snap-seccomp: run secondary-arch tests via gcc-multilib + - snap: implement `snap {repair,repairs}` and pass-through to snap- + repair + - interfaces/builtin: allow receiving dbus messages + - snap-repair: implement `snap-repair {done,skip,retry}` + - data/completion: small tweak to snap completion snippet + - dirs: fix classic support detection + - cmd/snap-repair: integrate root public keys for repairs + - tests: fix ubuntu core services + - tests: add new test that checks that the compat snapd-xdg-open + works + - snap-confine: improve error message if core/u-core cannot be found + - tests: only run tests/regression/nmcli on amd64 + - interfaces: mount host system fonts in desktop interface + - interfaces: enable partial apparmor support + - snapstate: auto-install missing base snaps + - spread: work around temporary packaging issue in debian sid + - asserts,cmd/snap-repair: introduce a mandatory summary for repairs + - asserts,cmd/snap-repair: represent RepairID internally as an int + - tests: test the real "xdg-open" from the core snap + - many: implement fetching sections and package names periodically. + - interfaces/network: allow using netcat as client + - snap-seccomp, osutil: use osutil.AtomicFile in snap-seccomp + - snap-seccomp: skip mknod syscall on arm64 + - tests: add trivial canonical-livepatch test + - tests: add test that ensures that all core services are working + - many: add logger.MockLogger() and use it in the tests + - snap-repair: fix test failure in TestRepairHitsTimeout + - asserts: add empty values check in HeadersFromPrimaryKey + - daemon: remove unused installSnap var in test + - daemon: reach for Overlord.Loop less thanks to overlord.Mock + - snap-seccomp: manually resolve socket() call in tests + - tests: change regex used to validate installed ubuntu core snap + - cmd/snapctl: allow snapctl -h without a context (regression fix). + - many: use snapcore/snapd/i18n instead of i18n/dumb + - many: introduce asserts.NotFoundError replacing both ErrNotFound + and store.AssertionNotFoundError + - packaging: don't include any marcos in comments + - overlord: use overlord.Mock in more tests, make sure we check the + outcome of Settle + - tests: try to fix staging tests + - store: simplify api base url config + - systemd: add systemd.MockJournalctl() + - many: provide systemd.MockSystemctl() helper + - tests: improve the listing test to not fail for e.g. 2.28~rc2 + - snapstate: give snapmgrTestSuite.settle() more time to settle + - tests: fix regex to check core version on snap list + - debian: update trusted account-keys check on 14.04 packaging + - interfaces: add udev netlink support to hardware-observe + - overlord: introduce Mock which enables to use Overlord.Settle for + settle in many more places + - snap-repair: execute the repair and capture logs/status + - tests: run the tests/unit/go everywhere + - daemon, snapstate: move ensureCore from daemon/api.go into + snapstate.go + - cmd/snap: get keys or root document + - spread.yaml: turn suse to manual given that it's breaking master + - many: configure store from state, reconfigure store at runtime + - osutil: AtomicWriter (an io.Writer), and io.Reader versions of + AtomicWrite* + - tests: check for negative syscalls in runBpf() and skip those + tests + - docs: use abolute path in PULL_REQUEST_TEMPLATE.md + - store: move device auth endpoint uris to config (#3831) + +* Fri Oct 13 2017 Michael Vogt +- New upstream release 2.28.5 + - snap-confine: cleanup broken nvidia udev tags + - cmd/snap-confine: update valid security tag regexp + - overlord/ifacestate: refresh udev backend on startup + - dbus: ensure io.snapcraft.Launcher.service is created on re- + exec + - snap-confine: add support for handling /dev/nvidia-modeset + - interfaces/network-control: remove incorrect rules for tun + +* Wed Oct 11 2017 Michael Vogt +- New upstream release 2.28.4 + - interfaces/opengl: don't udev tag nvidia devices and use snap- + confine instead + - debian: fix replaces/breaks for snap-xdg-open (thanks to apw!) + +* Wed Oct 11 2017 Michael Vogt +- New upstream release 2.28.3 + - interfaces/lxd: lxd slot implementation can also be an app + snap + +* Tue Oct 10 2017 Michael Vogt +- New upstream release 2.28.2 + - interfaces: fix udev rules for tun + - release,cmd,dirs: Redo the distro checks to take into account + distribution families + +* Wed Sep 27 2017 Michael Vogt +- New upstream release 2.28.1 + - snap-confine: update apparmor rules for fedora based basesnaps + - snapstate: rename refresh hook to post-refresh for consistency + +* Mon Sep 25 2017 Michael Vogt +- New upstream release 2.28 + - hooks: rename refresh to after-refresh + - snap-confine: bind mount /usr/lib/snapd relative to snap-confine + - cmd,dirs: treat "liri" the same way as "arch" + - snap-confine: fix base snaps on core + - hooks: substitute env vars when executing hooks + - interfaces: updates for default, browser-support, desktop, opengl, + upower and stub-resolv.conf + - cmd,dirs: treat manjaro the same as arch + - systemd: do not run auto-import and repair services on classic + - packaging/fedora: Ensure vendor/ is empty for builds and fix spec + to build current master + - many: fix TestSetConfNumber missing an Unlock and other fragility + improvements + - osutil: adjust StreamCommand tests for golang 1.9 + - daemon: allow polkit authorisation to install/remove snaps + - tests: make TestCmdWatch more robust + - debian: improve package description + - interfaces: add netlink kobject uevent to hardware observe + - debian: update trusted account-keys check on 14.04 packaging + - interfaces/network-{control,observe}: allow receiving + kobject_uevent() messages + - tests: fix lxd test for external backend + - snap-confine,snap-update-ns: add -no-pie to fix FTBFS on + go1.7,ppc64 + - corecfg: mock "systemctl" in all corecfg tests + - tests: fix unit tests on Ubuntu 14.04 + - debian: add missing flags when building static snap-exec + - many: end-to-end support for the bare base snap + - overlord/snapstate: SetRootDir from SetUpTest, not in just some + tests + - store: have an ad-hoc method on cfg to get its list of uris for + tests + - daemon: let client decide whether to allow interactive auth via + polkit + - client,daemon,snap,store: add license field + - overlord/snapstate: rename HasCurrent to IsInstalled, remove + superfluous/misleading check from All + - cmd/snap: SetRootDir from SetUpTest, not in just some individual + tests. + - systemd: rename snap-repair.{service,timer} to snapd.snap- + repair.{service,timer} + - snap-seccomp: remove use of x/net/bpf from tests + - httputil: more naive per go version way to recreate a default + transport for tls reconfig + - cmd/snap-seccomp/main_test.go: add one more syscall for arm64 + - interfaces/opengl: use == to compare, not = + - cmd/snap-seccomp/main_test.go: add syscalls for armhf and arm64 + - cmd/snap-repair: track and use a lower bound for the time for + TLS checks + - interfaces: expose bluez interface on classic OS + - snap-seccomp: add in-kernel bpf tests + - overlord: always try to get a serial, lazily on classic + - tests: add nmcli regression test + - tests: deal with __PNR_chown on aarch64 to fix FTBFS on arm64 + - tests: add autopilot-introspection interface test + - vendor: fix artifact from manually editing vendor/vendor.json + - tests: rename complexion to test-snapd-complexion + - interfaces: add desktop and desktop-legacy + interfaces/desktop: add new 'desktop' interface for modern DEs* + interfaces/builtin/desktop_test.go: use modern testing techniques* + interfaces/wayland: allow read on /etc/drirc for Plasma desktop* + interfaces/desktop-legacy: add new 'legacy' interface (currently + for a11y and input) + - tests: fix race in snap userd test + - devices/iio: add read/write for missing sysfs entries + - spread: don't set HTTPS?_PROXY for linode + - cmd/snap-repair: check signatures of repairs from Next + - env: set XDG_DATA_DIRS for wayland et.al. + - interfaces/{default,account-control}: Use username/group instead + of uid/gid + - interfaces/builtin: use udev tagging more broadly + - tests: add basic lxd test + - wrappers: ensure bash completion snaps install on core + - vendor: use old golang.org/x/crypto/ssh/terminal to build on + powerpc again + - docs: add PULL_REQUEST_TEMPLATE.md + - interfaces: fix network-manager plug + - hooks: do not error out when hook is optional and no hook handler + is registered + - cmd/snap: add userd command to replace snapd-xdg-open + - tests: new regex used to validate the core version on extra snaps + ass... + - snap: add new `snap switch` command + - tests: wait more and more debug info about fakestore start issues + - apparmor,release: add better apparmor detection/mocking code + - interfaces/i2c: adjust sysfs rule for alternate paths + - interfaces/apparmor: add missing call to dirs.SetRootDir + - cmd: "make hack" now also installs snap-update-ns + - tests: copy files with less verbosity + - cmd/snap-confine: allow using additional libraries required by + openSUSE + - packaging/fedora: Merge changes from Fedora Dist-Git + - snapstate: improve the error message when classic confinement is + not supported + - tests: add test to ensure amd64 can run i386 syscall binaries + - tests: adding extra info for fakestore when fails to start + - tests: install most important snaps + - cmd/snap-repair: more test coverage of filtering + - squashfs: remove runCommand/runCommandWithOutput as we do not need + it + - cmd/snap-repair: ignore superseded revisions, filter on arch and + models + - hooks: support for refresh hook + - Partial revert "overlord/devicestate, store: update device auth + endpoints URLs" + - cmd/snap-confine: allow reading /proc/filesystems + - cmd/snap-confine: genearlize apparmor profile for various lib + layout + - corecfg: fix proxy.* writing and add integration test + - corecfg: deal with system.power-key-action="" correctly + - vendor: update vendor.json after (presumed) manual edits + - cmd/snap: in `snap info`, don't print a newline between tracks + - daemon: add polkit support to /v2/login + - snapd,snapctl: decode json using Number + - client: fix go vet 1.7 errors + - tests: make 17.04 shellcheck clean + - tests: remove TestInterfacesHelp as it breaks when go-flags + changes + - snapstate: undo a daemon restart on classic if needed + - cmd/snap-repair: recover brand/model from + /var/lib/snapd/seed/assertions checking signatures and brand + account + - spread: opt into unsafe IO during spread tests + - snap-repair: update snap-repair/runner_test.go for API change in + makeMockServer + - cmd/snap-repair: skeleton code around actually running a repair + - tests: wait until the port is listening after start the fake store + - corecfg: fix typo in tests + - cmd/snap-repair: test that redirects works during fetching + - osutil: honor SNAPD_UNSAFE_IO for testing + - vendor: explode and make more precise our golang.go/x/crypto deps, + use same version as Debian unstable + - many: sanitize NewStoreStack signature, have shared default store + test private keys + - systemd: disable `Nice=-5` to fix error when running inside lxd + - spread.yaml: update delta ref to 2.27 + - cmd/snap-repair: use E-Tags when refetching a repair to retry + - interfaces/many: updates based on chromium and mrrescue denials + - cmd/snap-repair: implement most logic to get the next repair to + run/retry in a brand sequence + - asserts/assertstest: copy headers in SigningDB.Sign + - interfaces: convert uhid to common interface and test cases + improvement for time_control and opengl + - many tests: move all panicing fake store methods to a common place + - asserts: add store assertion type + - interfaces: don't crash if content slot has no attributes + - debian: do not build with -buildmode=pie on i386 + - wrappers: symlink completion snippets when symlinking binaries + - tests: adding more debug information for the interfaces-cups- + control … + - apparmor: pass --quiet to parser on load unless SNAPD_DEBUG is set + - many: allow and support serials signed by the 'generic' authority + instead of the brand + - corecfg: add proxy configuration via `snap set core + proxy.{http,https,ftp}=...` + - interfaces: a bunch of interfaces test improvement + - tests: enable regression and completion suites for opensuse + - tests: installing snapd for nested test suite + - interfaces: convert lxd_support to common iface + - interfaces: add missing test for camera interface. + - snap: add support for parsing snap layout section + - cmd/snap-repair: like for downloads we cannot have a timeout (at + least for now), less aggressive retry strategies + - overlord: rely on more conservative ensure interval + - overlord,store: no piles of return args for methods gathering + device session request params + - overlord,store: send model assertion when setting up device + sessions + - interfaces/misc: updates for unity7/x11, browser- + support, network-control and mount-observe + interfaces/unity7,x11: update for NETLINK_KOBJECT_UEVENT + interfaces/browser-support: update sysfs reads for + newer browser versions, interfaces/network-control: rw for + ieee80211 advanced wireless interfaces/mount-observe: allow read + on sysfs entries for block devices + - tests: use dnf --refresh install to avert stale cache + - osutil: ensure TestLockUnlockWorks uses supported flock + - interfaces: convert lxd to common iface + - tests: restart snapd to ensure re-exec settings are applied + - tests: fix interfaces-cups-control test + - interfaces: improve and tweak bunch of interfaces test cases. + - tests: adding extra worker for fedora + - asserts,overlord/devicestate: support predefined assertions that + don't establish foundational trust + - interfaces: convert two hardware_random interfaces to common iface + - interfaces: convert io_ports_control to common iface + - tests: fix for upgrade test on fedora + - daemon, client, cmd/snap: implement snap start/stop/restart + - cmd/snap-confine: set _FILE_OFFSET_BITS to 64 + - interfaces: covert framebuffer to commonInterface + - interfaces: convert joystick to common iface + - interfaces/builtin: add the spi interface + - wrappers, overlord/snapstate/backend: make link-snap clean up on + failure. + - interfaces/wayland: add wayland interface + - interfaces: convert kvm to common iface + - tests: extend upower-observe test to cover snaps providing slots + - tests: enable main suite for opensuse + - interfaces: convert physical_memory_observe to common iface + - interfaces: add missing test for optical_drive interface. + - interfaces: convert physical_memory_control to common iface + - interfaces: convert ppp to common iface + - interfaces: convert time-control to common iface + - tests: fix failover test + - interfaces/builtin: rework for avahi interface + - interfaces: convert broadcom-asic-control to common iface + - snap/snapenv: document the use of CoreSnapMountDir for SNAP + - packaging/arch: drop patches merged into master + - cmd: fix mustUnsetenv docstring (thanks to Chipaca) + - release: remove default from VERSION_ID + - tests: enable regression, upgrade and completion test suites for + fedora + - tests: restore interfaces-account-control properly + - overlord/devicestate, store: update device auth endpoints URLs + - tests: fix install-hook test failure + - tests: download core and ubuntu-core at most once + - interfaces: add common support for udev + - overlord/devicestate: fix, don't assume that the serial is backed + by a 1-key chain + - cmd/snap-confine: don't share /etc/nsswitch from host + - store: do not resume a download when we already have the whole + thing + - many: implement "snap logs" + - store: don't call useDeltas() twice in quick succession + - interfaces/builtin: add kvm interface + - snap/snapenv: always expect /snap for $SNAP + - cmd: mark arch as non-reexecing distro + - cmd: fix tests that assume /snap mount + - gitignore: ignore more build artefacts + - packaging: add current arch packaging + - interfaces/unity7: allow receiving media key events in (at least) + gnome-shell + - interfaces/many, cmd/snap-confine: miscellaneous policy updates + - interfaces/builtin: implement broadcom-asic-control interface + - interfaces/builtin: reduce duplication and remove cruft in + Sanitize{Plug,Slot} + - tests: apply underscore convention for SNAPMOUNTDIR variable + - interfaces/greengrass-support: adjust accesses now that have + working snap + - daemon, client, cmd/snap: implement "snap services" + - tests: fix refresh tests not stopping fake store for fedora + - many: add the interface command + - overlord/snapstate/backend: some copydata improvements + - many: support querying and completing assertion type names + - interfaces/builtin: discard empty Validate{Plug,Slot} + - cmd/snap-repair: start of Runner, implement first pass of Peek + and Fetch + - tests: enable main suite on fedora + - snap: do not always quote the snap info summary + - vendor: update go-flags to address crash in "snap debug" + - interfaces: opengl support pci device and vendor + - many: start implenting "base" snap type on the snapd side + - arch,release: map armv6 correctly + - many: expose service status in 'snap info' + - tests: add browser-support interface test + - tests: disable snapd-notify for the external backend + - interfaces: Add /run/uuid/request to openvswitch + - interfaces: add password-manager-service implicit classic + interface + - cmd: rework reexec detection + - cmd: fix re-exec bug when starting from snapd 2.21 + - tests: dependency packages installed during prepare-project + - tests: remove unneeded check for re-exec in InternalToolPath() + - cmd,tests: fix classic confinement confusing re-execution code + - store: configurable base api + - tests: fix how package lists are updated for opensuse and fedora + +* Thu Sep 07 2017 Michael Vogt +- New upstream release 2.27.6 + - interfaces: add udev netlink support to hardware-observe + - interfaces/network-{control,observe}: allow receiving + kobject_uevent() messages + +* Wed Aug 30 2017 Michael Vogt +- New upstream release 2.27.5 + - interfaces: fix network-manager plug regression + - hooks: do not error when hook handler is not registered + - interfaces/alsa,pulseaudio: allow read on udev data for sound + - interfaces/optical-drive: read access to udev data for /dev/scd* + - interfaces/browser-support: read on /proc/vmstat and misc udev + data + +* Thu Aug 24 2017 Michael Vogt +- New upstream release 2.27.4 + - snap-seccomp: add secondary arch for unrestricted snaps as well + +* Fri Aug 18 2017 Michael Vogt +- New upstream release 2.27.3 + - systemd: disable `Nice=-5` to fix error when running inside lxdSee + https://bugs.launchpad.net/snapd/+bug/1709536 + +* Wed Aug 16 2017 Neal Gompa - 2.27.2-2 +- Bump to rebuild for F27 and Rawhide + +* Wed Aug 16 2017 Neal Gompa - 2.27.2-1 +- Release 2.27.2 to Fedora (RH#1482173) + +* Wed Aug 16 2017 Michael Vogt +- New upstream release 2.27.2 + - tests: remove TestInterfacesHelp as it breaks when go-flags + changes + - interfaces: don't crash if content slot has no attributes + - debian: do not build with -buildmode=pie on i386 + - interfaces: backport broadcom-asic-control interface + - interfaces: allow /usr/bin/xdg-open in unity7 + - store: do not resume a download when we already have the whole + thing + +* Mon Aug 14 2017 Neal Gompa - 2.27.1-1 +- Release 2.27.1 to Fedora (RH#1481247) + +* Mon Aug 14 2017 Michael Vogt +- New upstream release 2.27.1 + - tests: use dnf --refresh install to avert stale cache + - tests: fix test failure on 14.04 due to old version of + flock + - updates for unity7/x11, browser-support, network-control, + mount-observe + - interfaces/unity7,x11: update for NETLINK_KOBJECT_UEVENT + - interfaces/browser-support: update sysfs reads for + newer browser versions + - interfaces/network-control: rw for ieee80211 advanced wireless + - interfaces/mount-observe: allow read on sysfs entries for block + devices + +* Thu Aug 10 2017 Neal Gompa - 2.27-1 +- Release 2.27 to Fedora (RH#1458086) + +* Thu Aug 10 2017 Michael Vogt +- New upstream release 2.27 + - fix build failure on 32bit fedora + - interfaces: add password-manager-service implicit classic interface + - interfaces/greengrass-support: adjust accesses now that have working + snap + - interfaces/many, cmd/snap-confine: miscellaneous policy updates + - interfaces/unity7: allow receiving media key events in (at least) + gnome-shell + - cmd: fix re-exec bug when starting from snapd 2.21 + - tests: restore interfaces-account-control properly + - cmd: fix tests that assume /snap mount + - cmd: mark arch as non-reexecing distro + - snap-confine: don't share /etc/nsswitch from host + - store: talk to api.snapcraft.io for purchases + - hooks: support for install and remove hooks + - packaging: fix Fedora support + - tests: add bluetooth-control interface test + - store: talk to api.snapcraft.io for assertions + - tests: remove snapd before building from branch + - tests: add avahi-observe interface test + - store: orders API now checks if customer is ready + - cmd/snap: snap find only searches stable + - interfaces: updates default, mir, optical-observe, system-observe, + screen-inhibit-control and unity7 + - tests: speedup prepare statement part 1 + - store: do not send empty refresh requests + - asserts: fix error handling in snap-developer consistency check + - systemd: add explicit sync to snapd.core-fixup.sh + - snapd: generate snap cookies on startup + - cmd,client,daemon: expose "force devmode" in sysinfo + - many: introduce and use strutil.ListContains and also + strutil.SortedListContains + - assserts,overlord/assertstate: test we don't accept chains of + assertions founded on a self-signed key coming externally + - interfaces: enable access to bridge settings + - interfaces: fix copy-pasted iio vs io in io-ports-control + - cmd/snap-confine: various small fixes and tweaks to seccomp + support code + - interfaces: bring back seccomp argument filtering + - systemd, osutil: rework systemd logs in preparation for services + commands + - tests: store /etc/systemd/system/snap-*core*.mount in snapd- + state.tar.gz + - tests: shellcheck improvements for tests/main tasks - first set of + tests + - cmd/snap: `--last` for abort and watch, and aliases + (search→find, change→tasks) + - tests: shellcheck improvements for tests/lib scripts + - tests: create ramdisk if it's not present + - tests: shellcheck improvements for nightly upgrade and regressions + tests + - snapd: fix for snapctl get panic on null config values. + - tests: fix for rng-tools service not restarting + - systemd: add snapd.core-fixup.service unit + - cmd: avoid using current symlink in InternalToolPath + - tests: fix timeout issue for test refresh core with hanging … + - intefaces: control bridged vlan/ppoe-tagged traffic + - cmd/snap: include snap type in notes + - overlord/state: Abort() only visits each task once + - tests: extend find-private test to cover more cases + - snap-seccomp: skip socket() tests on systems that use socketcall() + instead of socket() + - many: support snap title as localized/title-cased name + - snap-seccomp: deal with mknod on aarch64 in the seccomp tests + - interfaces: put base policy fragments inside each interface + - asserts: introduce NewDecoderWithTypeMaxBodySize + - tests: fix snapd-notify when it takes more time to restart + - snap-seccomp: fix snap-seccomp tests in artful + - tests: fix for create-key task to avoid rng-tools service ramains + alive + - snap-seccomp: make sure snap-seccomp writes the bpf file + atomically + - tests: do not disable ipv6 on core systems + - arch: the kernel architecture name is armv7l instead of armv7 + - snap-confine: ensure snap-confine waits some seconds for seccomp + security profiles + - tests: shellcheck improvements for tests/nested tasks + - wrappers: add SyslogIdentifier to the service unit files. + - tests: shellcheck improvements for unit tasks + - asserts: implement FindManyTrusted as well + - asserts: open up and optimize Encoder to help avoiding unnecessary + copying + - interfaces: simplify snap-confine by just loading pre-generated + bpf code + - tests: restart rng-tools services after few seconds + - interfaces, tests: add mising dbus abstraction to system-observe + and extend spread test + - store: change main store host to api.snapcraft.io + - overlord/cmdstate: new package for running commands as tasks. + - spread: help libapt resolve installing libudev-dev + - tests: show the IP from .travis.yaml + - tests/main: use pkgdb function in more test cases + - cmd,daemon: add debug command for displaying the base policy + - tests: prevent quoting error on opensuse + - tests: fix nightly suite + - tests: add linode-sru backend + - snap-confine: validate SNAP_NAME against security tag + - tests: fix ipv6 disable for ubuntu-core + - tests: extend core-revert test to cover bluez issues + - interfaces/greengrass-support: add support for Amazon Greengrass + as a snap + - asserts: support timestamp and optional disabled header on repair + - tests: reboot after upgrading to snapd on the -proposed pocket + - many: fix test cases to work with different DistroLibExecDir + - tests: reenable help test on ubuntu and debian systems + - packaging/{opensuse,fedora}: allow package build with testkeys + included + - tests/lib: generalize RPM build support + - interfaces/builtin: sync connected slot and permanent slot snippet + - tests: fix snap create-key by restarting automatically rng-tools + - many: switch to use http numeric statuses as agreed + - debian: add missing Type=notify in 14.04 packaging + - tests: mark interfaces-openvswitch as manual due to prepare errors + - debian: unify built_using between the 14.04 and 16.04 packaging + branch + - tests: pull from urandom when real entropy is not enough + - tests/main/manpages: install missing man package + - tests: add refresh --time output check + - debian: add missing "make -C data/systemd clean" + - tests: fix for upgrade test when it is repeated + - tests/main: use dir abstraction in a few more test cases + - tests/main: check for confinement in a few more interface tests + - spread: add fedora snap bin dir to global PATH + - tests: check that locale-control is not present on core + - many: snapctl outside hooks + - tests: add whoami check + - interfaces: compose the base declaration from interfaces + - tests: fix spread flaky tests linode + - tests,packaging: add package build support for openSUSE + - many: slight improvement of some snap error messaging + - errtracker: Include /etc/apparmor.d/usr.lib.snap-confine md5sum in + err reports + - tests: fix for the test postrm-purge + - tests: restoring the /etc/environment and service units config for + each test + - daemon: make snapd a "Type=notify" daemon and notify when startup + is done + - cmd/snap-confine: add support for --base snap + - many: derive implicit slots from interface meta-data + - tests: add core revert test + - tests,packaging: add package build support for Fedora for our + spread setup + - interfaces: move base declaration to the policy sub-package + - tests: fix for snapd-reexec test cheking for restart info on debug + log + - tests: show available entropy on error + - tests: clean journalctl logs on trusty + - tests: fix econnreset on staging + - tests: modify core before calling set + - tests: add snap-confine privilege test + - tests: add staging snap-id + - interfaces/builtin: silence ptrace denial for network-manager + - tests: add alsa interface spread test + - tests: prefer ipv4 over ipv6 + - tests: fix for econnreset test checking that the download already + started + - httputil,store: extract retry code to httputil, reorg usages + - errtracker: report if snapd did re-execute itself + - errtracker: include bits of snap-confine apparmor profile + - tests: take into account staging snap-ids for snap-info + - cmd: add stub new snap-repair command and add timer + - many: stop "snap refresh $x --channel invalid" from working + - interfaces: revert "interfaces: re-add reverted ioctl and quotactl + - snapstate: consider connect/disconnect tasks in + CheckChangeConflict. + - interfaces: disable "mknod |N" in the default seccomp template + again + - interfaces,overlord/ifacestate: make sure installing slots after + plugs works similarly to plugs after slots + - interfaces/seccomp: add bind() syscall for forced-devmode systems + - packaging/fedora: Sync packaging from Fedora Dist-Git + - tests: move static and unit tests to spread task + - many: error types should be called FooError, not ErrFoo. + - partition: add directory sync to the save uboot.env file code + - cmd: test everything (100% coverage \o/) + - many: make shell scripts shellcheck-clean + - tests: remove additional setup for docker on core + - interfaces: add summary to each interface + - many: remove interface meta-data from list of connections + - logger (& many more, to accommodate): drop explicit syslog. + - packaging: import packaging bits for opensuse + - snapstate,many: implement snap install --unaliased + - tests/lib: abstract build dependency installation a bit more + - interfaces, osutil: move flock code from interfaces/mount to + osutil + - cmd: auto import assertions only from ext4,vfat file systems + - many: refactor in preparation for 'snap start' + - overlord/snapstate: have an explicit code path last-refresh + unset/zero => immediately refresh try + - tests: fixes for executions using the staging store + - tests: use pollinate to seed the rng + - cmd/snap,tests: show the sha3-384 of the snap for snap info + --verbose SNAP-FILE + - asserts: simplify and adjust repair assertion definition + - cmd/snap,tests: show the snap id if available in snap info + - daemon,overlord/auth: store from model assertion wins + - cmd/snap,tests/main: add confinement switch instead of spread + system blacklisting + - many: cleanup MockCommands and don't leave a process around after + hookstate tests + - tests: update listing test to the core version number schema + - interfaces: allow snaps to use the timedatectl utility + - packaging: Add Fedora packaging files + - tests/libs: add distro_auto_remove_packages function + - cmd/snap: correct devmode note for anomalous state + - tests/main/snap-info: use proper pkgdb functions to install distro + packages + - tests/lib: use mktemp instead of tempfile to work cross-distro + - tests: abstract common dirs which differ on distributions + - many: model and expose interface meta-data. + - overlord: make config defaults from gadget work also at first boot + - interfaces/log-observe: allow using journalctl from hostfs for + classic distro + - partition,snap: add support for android boot + - errtracker: small simplification around readMachineID + - snap-confine: move rm_rf_tmp to test-utils. + - tests/lib: introduce pkgdb helper library + - errtracker: try multiple paths to read machine-id + - overlord/hooks: make sure only one hook for given snap is executed + at a time. + - cmd/snap-confine: use SNAP_MOUNT_DIR to setup /snap inside the + confinement env + - tests: bump kill-timeout and remove quiet call on build + - tests/lib/snaps: add a test store snap with a passthrough + configure hook + - daemon: teach the daemon to wait on active connections when + shutting down + - tests: remove unit tests task + - tests/main/completion: source from /usr/share/bash-completion + - assertions: add "repair" assertion + - interfaces/seccomp: document Backend.NewSpecification + - wrappers: make StartSnapServices cleanup any services that were + added if a later one fails + - overlord/snapstate: avoid creating command aliases for daemons + - vendor: remove unused packages + - vendor,partition: fix panics from uenv + - cmd,interfaces/mount: run snap-update-ns and snap-discard-ns from + core if possible + - daemon: do not allow to install ubuntu-core anymore + - wrappers: service start/stop were inconsistent + - tests: fix failing tests (snap core version, syslog changes) + - cmd/snap-update-ns: add actual implementation + - tests: improve entropy also for ubuntu + - cmd/snap-confine: use /etc/ssl from the core snap + - wrappers: don't convert between []byte and string needlessly. + - hooks: default timeout + - overlord/snapstate: Enable() was ignoring the flags from the + snap's state, resulting in losing "devmode" on disable/enable. + - difs,interfaces/mount: add support for locking namespaces + - interfaces/mount: keep track of kept mount entries + - tests/main: move a bunch of greps over to MATCH + - interfaces/builtin: make all interfaces private + - interfaces/mount: spell unmount correctly + - tests: allow 16-X.Y.Z version of core snap + - the timezone_control interface only allows changing /etc/timezone + and /etc/writable/timezone. systemd-timedated also updated the + link of /etc/localtime and /etc/writable/localtime ... allow + access to this file too + - cmd/snap-confine: aggregate operations holding global lock + - api, ifacestate: resolve disconnect early + - interfaces/builtin: ensure we don't register interfaces twice + +* Thu Aug 03 2017 Fedora Release Engineering - 2.26.3-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 2.26.3-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Thu May 25 2017 Neal Gompa - 2.26.3-3 +- Cover even more stuff for proper erasure on final uninstall (RH#1444422) + +* Sun May 21 2017 Neal Gompa - 2.26.3-2 +- Fix error in script for removing Snappy content (RH#1444422) +- Adjust changelog bug references to be specific on origin + +* Wed May 17 2017 Neal Gompa - 2.26.3-1 +- Update to snapd 2.26.3 +- Drop merged and unused patches +- Cover more Snappy content for proper erasure on final uninstall (RH#1444422) +- Add temporary fix to ensure generated seccomp profiles don't break snapctl + +* Mon May 01 2017 Neal Gompa - 2.25-1 +- Update to snapd 2.25 +- Ensure all Snappy content is gone on final uninstall (RH#1444422) + +* Tue Apr 11 2017 Neal Gompa - 2.24-1 +- Update to snapd 2.24 +- Drop merged patches +- Install snap bash completion and snapd info file + +* Wed Apr 05 2017 Neal Gompa - 2.23.6-4 +- Test if snapd socket and timer enabled and start them if enabled on install + +* Sat Apr 01 2017 Neal Gompa - 2.23.6-3 +- Fix profile.d generation so that vars aren't expanded in package build + +* Fri Mar 31 2017 Neal Gompa - 2.23.6-2 +- Fix the overlapping file conflicts between snapd and snap-confine +- Rework package descriptions slightly + +* Thu Mar 30 2017 Neal Gompa - 2.23.6-1 +- Rebase to snapd 2.23.6 +- Rediff patches +- Re-enable seccomp +- Fix building snap-confine on 32-bit arches +- Set ExclusiveArch based on upstream supported arch list + +* Wed Mar 29 2017 Neal Gompa - 2.23.5-1 +- Rebase to snapd 2.23.5 +- Disable seccomp temporarily avoid snap-confine bugs (LP#1674193) +- Use vendorized build for non-Fedora + +* Mon Mar 13 2017 Neal Gompa - 2.23.1-1 +- Rebase to snapd 2.23.1 +- Add support for vendored tarball for non-Fedora targets +- Use merged in SELinux policy module + +* Sat Feb 11 2017 Fedora Release Engineering - 2.16-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Wed Oct 19 2016 Zygmunt Krynicki - 2.16-1 +- New upstream release + +* Tue Oct 18 2016 Neal Gompa - 2.14-2 +- Add SELinux policy module subpackage + +* Tue Aug 30 2016 Zygmunt Krynicki - 2.14-1 +- New upstream release + +* Tue Aug 23 2016 Zygmunt Krynicki - 2.13-1 +- New upstream release + +* Thu Aug 18 2016 Zygmunt Krynicki - 2.12-2 +- Correct license identifier + +* Thu Aug 18 2016 Zygmunt Krynicki - 2.12-1 +- New upstream release + +* Thu Aug 18 2016 Zygmunt Krynicki - 2.11-8 +- Add %%dir entries for various snapd directories +- Tweak Source0 URL + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-7 +- Disable snapd re-exec feature by default + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-6 +- Don't auto-start snapd.socket and snapd.refresh.timer + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-5 +- Don't touch snapd state on removal + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-4 +- Use ExecStartPre to load squashfs.ko before snapd starts +- Use dedicated systemd units for Fedora + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-3 +- Remove systemd preset (will be requested separately according to distribution + standards). + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-2 +- Use Requires: kmod(squashfs.ko) instead of Requires: kernel-modules + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-1 +- New upstream release +- Move private executables to /usr/libexec/snapd/ + +* Fri Jun 24 2016 Zygmunt Krynicki - 2.0.9-2 +- Depend on kernel-modules to ensure that squashfs can be loaded. Load it afer + installing the package. This hopefully fixes + https://github.com/zyga/snapcore-fedora/issues/2 + +* Fri Jun 17 2016 Zygmunt Krynicki - 2.0.9 +- New upstream release + https://github.com/snapcore/snapd/releases/tag/2.0.9 + +* Tue Jun 14 2016 Zygmunt Krynicki - 2.0.8.1 +- New upstream release + +* Fri Jun 10 2016 Zygmunt Krynicki - 2.0.8 +- First package for Fedora diff -Nru snapd-2.28.5/packaging/fedora-27/snap-mgmt.sh snapd-2.29.3/packaging/fedora-27/snap-mgmt.sh --- snapd-2.28.5/packaging/fedora-27/snap-mgmt.sh 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/packaging/fedora-27/snap-mgmt.sh 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,137 @@ +#!/bin/bash + +# Overlord management of snapd for package manager actions. +# Implements actions that would be invoked in %pre(un) actions for snapd. +# Derived from the snapd.postrm scriptlet used in the Ubuntu packaging for +# snapd. + +set -e + +SNAP_MOUNT_DIR="/var/lib/snapd/snap" + +show_help() { + exec cat <<'EOF' +Usage: snap-mgmt.sh [OPTIONS] + +A simple script to cleanup snap installations. + +optional arguments: + --help Show this help message and exit + --snap-mount-dir= Provide a path to be used as $SNAP_MOUNT_DIR + --purge Purge all data from $SNAP_MOUNT_DIR +EOF +} + +SNAP_UNIT_PREFIX="$(systemd-escape -p ${SNAP_MOUNT_DIR})" + +systemctl_stop() { + unit="$1" + if systemctl is-active -q "$unit"; then + echo "Stoping $unit" + systemctl stop -q "$unit" || true + fi +} + +purge() { + # undo any bind mount to ${SNAP_MOUNT_DIR} that resulted from LP:#1668659 + if grep -q "${SNAP_MOUNT_DIR} ${SNAP_MOUNT_DIR}" /proc/self/mountinfo; then + umount -l "${SNAP_MOUNT_DIR}" || true + fi + + mounts=$(systemctl list-unit-files --full | grep "^${SNAP_UNIT_PREFIX}[-.].*\.mount" | cut -f1 -d ' ') + services=$(systemctl list-unit-files --full | grep "^${SNAP_UNIT_PREFIX}[-.].*\.service" | cut -f1 -d ' ') + for unit in $services $mounts; do + # ensure its really a snap mount unit or systemd unit + if ! grep -q 'What=/var/lib/snapd/snaps/' "/etc/systemd/system/$unit" && ! grep -q 'X-Snappy=yes' "/etc/systemd/system/$unit"; then + echo "Skipping non-snapd systemd unit $unit" + continue + fi + + echo "Stopping $unit" + systemctl_stop "$unit" + + # if it is a mount unit, we can find the snap name in the mount + # unit (we just ignore unit files) + snap=$(grep "Where=${SNAP_MOUNT_DIR}/" "/etc/systemd/system/$unit"|cut -f3 -d/) + rev=$(grep "Where=${SNAP_MOUNT_DIR}/" "/etc/systemd/system/$unit"|cut -f4 -d/) + if [ -n "$snap" ]; then + echo "Removing snap $snap" + # aliases + if [ -d "${SNAP_MOUNT_DIR}/bin" ]; then + find "${SNAP_MOUNT_DIR}/bin" -maxdepth 1 -lname "$snap" -delete + find "${SNAP_MOUNT_DIR}/bin" -maxdepth 1 -lname "$snap.*" -delete + fi + # generated binaries + rm -f "${SNAP_MOUNT_DIR}/bin/$snap" + rm -f "${SNAP_MOUNT_DIR}/bin/$snap".* + # snap mount dir + umount -l "${SNAP_MOUNT_DIR}/$snap/$rev" 2> /dev/null || true + rm -rf "${SNAP_MOUNT_DIR:?}/$snap/$rev" + rm -f "${SNAP_MOUNT_DIR}/$snap/current" + # snap data dir + rm -rf "/var/snap/$snap/$rev" + rm -rf "/var/snap/$snap/common" + rm -f "/var/snap/$snap/current" + # opportunistic remove (may fail if there are still revisions left) + for d in "${SNAP_MOUNT_DIR}/$snap" "/var/snap/$snap"; do + if [ -d "$d" ]; then + rmdir --ignore-fail-on-non-empty "$d" + fi + done + fi + + echo "Removing $unit" + rm -f "/etc/systemd/system/$unit" + rm -f "/etc/systemd/system/multi-user.target.wants/$unit" + done + + echo "Discarding preserved snap namespaces" + # opportunistic as those might not be actually mounted + for mnt in /run/snapd/ns/*.mnt; do + umount -l "$mnt" || true + rm -f "$mnt" + done + for fstab in /run/snapd/ns/*.fstab; do + rm -f "$fstab" + done + umount -l /run/snapd/ns/ || true + + + echo "Removing downloaded snaps" + rm -rf /var/lib/snapd/snaps/* + + echo "Final directory cleanup" + rm -rf "${SNAP_MOUNT_DIR}" + rm -rf /var/snap + + echo "Removing leftover snap shared state data" + rm -rf /var/lib/snapd/desktop/applications/* + rm -rf /var/lib/snapd/seccomp/bpf/* + rm -rf /var/lib/snapd/device/* + rm -rf /var/lib/snapd/assertions/* + + echo "Removing snapd catalog cache" + rm -f /var/cache/snapd/* +} + +while [ -n "$1" ]; do + case "$1" in + --help) + show_help + exit + ;; + --snap-mount-dir=*) + SNAP_MOUNT_DIR=${1#*=} + SNAP_UNIT_PREFIX=$(systemd-escape -p "$SNAP_MOUNT_DIR") + shift + ;; + --purge) + purge + shift + ;; + *) + echo "Unknown command: $1" + exit 1 + ;; + esac +done diff -Nru snapd-2.28.5/packaging/fedora-rawhide/snapd.spec snapd-2.29.3/packaging/fedora-rawhide/snapd.spec --- snapd-2.28.5/packaging/fedora-rawhide/snapd.spec 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/packaging/fedora-rawhide/snapd.spec 2017-11-09 18:16:16.000000000 +0000 @@ -0,0 +1,1630 @@ +# With Fedora, nothing is bundled. For everything else, bundling is used. +# To use bundled stuff, use "--with vendorized" on rpmbuild +%if 0%{?fedora} +%bcond_with vendorized +%else +%bcond_without vendorized +%endif + +# A switch to allow building the package with support for testkeys which +# are used for the spread test suite of snapd. +%bcond_with testkeys + +%global with_devel 1 +%global with_debug 1 +%global with_check 0 +%global with_unit_test 0 +%global with_test_keys 0 + +# For the moment, we don't support all golang arches... +%global with_goarches 0 + +%if ! %{with vendorized} +%global with_bundled 0 +%else +%global with_bundled 1 +%endif + +%if ! %{with testkeys} +%global with_test_keys 0 +%else +%global with_test_keys 1 +%endif + +%if 0%{?with_debug} +%global _dwz_low_mem_die_limit 0 +%else +%global debug_package %{nil} +%endif + +%global provider github +%global provider_tld com +%global project snapcore +%global repo snapd +# https://github.com/snapcore/snapd +%global provider_prefix %{provider}.%{provider_tld}/%{project}/%{repo} +%global import_path %{provider_prefix} + +%global snappy_svcs snapd.service snapd.socket snapd.autoimport.service snapd.refresh.timer snapd.refresh.service + +Name: snapd +Version: 2.29.3 +Release: 0%{?dist} +Summary: A transactional software package manager +Group: System Environment/Base +License: GPLv3 +URL: https://%{provider_prefix} +%if ! 0%{?with_bundled} +Source0: https://%{provider_prefix}/archive/%{version}/%{name}-%{version}.tar.gz +%else +Source0: https://%{provider_prefix}/releases/download/%{version}/%{name}_%{version}.vendor.orig.tar.xz +%endif + +%if 0%{?with_goarches} +# e.g. el6 has ppc64 arch without gcc-go, so EA tag is required +ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}} +%else +# Verified arches from snapd upstream +ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 ppc64le s390x +%endif + +# If go_compiler is not set to 1, there is no virtual provide. Use golang instead. +BuildRequires: %{?go_compiler:compiler(go-compiler)}%{!?go_compiler:golang} +BuildRequires: systemd +%{?systemd_requires} + +Requires: snap-confine%{?_isa} = %{version}-%{release} +Requires: squashfs-tools +# we need squashfs.ko loaded +Requires: kmod(squashfs.ko) +# bash-completion owns /usr/share/bash-completion/completions +Requires: bash-completion + +# Force the SELinux module to be installed +Requires: %{name}-selinux = %{version}-%{release} + +%if ! 0%{?with_bundled} +BuildRequires: golang(github.com/cheggaaa/pb) +BuildRequires: golang(github.com/coreos/go-systemd/activation) +BuildRequires: golang(github.com/godbus/dbus) +BuildRequires: golang(github.com/godbus/dbus/introspect) +BuildRequires: golang(github.com/gorilla/mux) +BuildRequires: golang(github.com/jessevdk/go-flags) +BuildRequires: golang(github.com/mvo5/uboot-go/uenv) +BuildRequires: golang(github.com/ojii/gettext.go) +BuildRequires: golang(github.com/seccomp/libseccomp-golang) +BuildRequires: golang(golang.org/x/crypto/openpgp/armor) +BuildRequires: golang(golang.org/x/crypto/openpgp/packet) +BuildRequires: golang(golang.org/x/crypto/sha3) +BuildRequires: golang(golang.org/x/crypto/ssh/terminal) +BuildRequires: golang(golang.org/x/net/context) +BuildRequires: golang(golang.org/x/net/context/ctxhttp) +BuildRequires: golang(gopkg.in/check.v1) +BuildRequires: golang(gopkg.in/macaroon.v1) +BuildRequires: golang(gopkg.in/mgo.v2/bson) +BuildRequires: golang(gopkg.in/retry.v1) +BuildRequires: golang(gopkg.in/tomb.v2) +BuildRequires: golang(gopkg.in/yaml.v2) +%endif + +%description +Snappy is a modern, cross-distribution, transactional package manager +designed for working with self-contained, immutable packages. + +%package -n snap-confine +Summary: Confinement system for snap applications +License: GPLv3 +Group: System Environment/Base +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: libtool +BuildRequires: gcc +BuildRequires: gettext +BuildRequires: gnupg +BuildRequires: indent +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(libcap) +BuildRequires: pkgconfig(libseccomp) +BuildRequires: pkgconfig(libudev) +BuildRequires: pkgconfig(systemd) +BuildRequires: pkgconfig(udev) +BuildRequires: xfsprogs-devel +BuildRequires: glibc-static +BuildRequires: libseccomp-static +BuildRequires: valgrind +BuildRequires: %{_bindir}/rst2man +%if 0%{?fedora} >= 25 +# ShellCheck in F24 and older doesn't work +BuildRequires: %{_bindir}/shellcheck +%endif + +# Ensures older version from split packaging is replaced +Obsoletes: snap-confine < 2.19 + +%description -n snap-confine +This package is used internally by snapd to apply confinement to +the started snap applications. + +%package selinux +Summary: SELinux module for snapd +Group: System Environment/Base +License: GPLv2+ +BuildArch: noarch +BuildRequires: selinux-policy, selinux-policy-devel +Requires(post): selinux-policy-base >= %{_selinux_policy_version} +Requires(post): policycoreutils +Requires(post): policycoreutils-python-utils +Requires(pre): libselinux-utils +Requires(post): libselinux-utils + +%description selinux +This package provides the SELinux policy module to ensure snapd +runs properly under an environment with SELinux enabled. + + +%if 0%{?with_devel} +%package devel +Summary: %{summary} +BuildArch: noarch + +%if 0%{?with_check} && ! 0%{?with_bundled} +%endif + +%if ! 0%{?with_bundled} +Requires: golang(github.com/cheggaaa/pb) +Requires: golang(github.com/coreos/go-systemd/activation) +Requires: golang(github.com/godbus/dbus) +Requires: golang(github.com/godbus/dbus/introspect) +Requires: golang(github.com/gorilla/mux) +Requires: golang(github.com/jessevdk/go-flags) +Requires: golang(github.com/mvo5/uboot-go/uenv) +Requires: golang(github.com/ojii/gettext.go) +Requires: golang(github.com/seccomp/libseccomp-golang) +Requires: golang(golang.org/x/crypto/openpgp/armor) +Requires: golang(golang.org/x/crypto/openpgp/packet) +Requires: golang(golang.org/x/crypto/sha3) +Requires: golang(golang.org/x/crypto/ssh/terminal) +Requires: golang(golang.org/x/net/context) +Requires: golang(golang.org/x/net/context/ctxhttp) +Requires: golang(gopkg.in/check.v1) +Requires: golang(gopkg.in/macaroon.v1) +Requires: golang(gopkg.in/mgo.v2/bson) +Requires: golang(gopkg.in/retry.v1) +Requires: golang(gopkg.in/tomb.v2) +Requires: golang(gopkg.in/yaml.v2) +%else +# These Provides are unversioned because the sources in +# the bundled tarball are unversioned (they go by git commit) +# *sigh*... I hate golang... +Provides: bundled(golang(github.com/cheggaaa/pb)) +Provides: bundled(golang(github.com/coreos/go-systemd/activation)) +Provides: bundled(golang(github.com/godbus/dbus)) +Provides: bundled(golang(github.com/godbus/dbus/introspect)) +Provides: bundled(golang(github.com/gorilla/mux)) +Provides: bundled(golang(github.com/jessevdk/go-flags)) +Provides: bundled(golang(github.com/mvo5/uboot-go/uenv)) +Provides: bundled(golang(github.com/mvo5/libseccomp-golang)) +Provides: bundled(golang(github.com/ojii/gettext.go)) +Provides: bundled(golang(golang.org/x/crypto/openpgp/armor)) +Provides: bundled(golang(golang.org/x/crypto/openpgp/packet)) +Provides: bundled(golang(golang.org/x/crypto/sha3)) +Provides: bundled(golang(golang.org/x/crypto/ssh/terminal)) +Provides: bundled(golang(golang.org/x/net/context)) +Provides: bundled(golang(golang.org/x/net/context/ctxhttp)) +Provides: bundled(golang(gopkg.in/check.v1)) +Provides: bundled(golang(gopkg.in/macaroon.v1)) +Provides: bundled(golang(gopkg.in/mgo.v2/bson)) +Provides: bundled(golang(gopkg.in/retry.v1)) +Provides: bundled(golang(gopkg.in/tomb.v2)) +Provides: bundled(golang(gopkg.in/yaml.v2)) +%endif + +# Generated by gofed +Provides: golang(%{import_path}/arch) = %{version}-%{release} +Provides: golang(%{import_path}/asserts) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/assertstest) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/signtool) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/snapasserts) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/sysdb) = %{version}-%{release} +Provides: golang(%{import_path}/asserts/systestkeys) = %{version}-%{release} +Provides: golang(%{import_path}/boot) = %{version}-%{release} +Provides: golang(%{import_path}/boot/boottest) = %{version}-%{release} +Provides: golang(%{import_path}/client) = %{version}-%{release} +Provides: golang(%{import_path}/cmd) = %{version}-%{release} +Provides: golang(%{import_path}/daemon) = %{version}-%{release} +Provides: golang(%{import_path}/dirs) = %{version}-%{release} +Provides: golang(%{import_path}/errtracker) = %{version}-%{release} +Provides: golang(%{import_path}/httputil) = %{version}-%{release} +Provides: golang(%{import_path}/i18n) = %{version}-%{release} +Provides: golang(%{import_path}/image) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/apparmor) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/backends) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/builtin) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/dbus) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/ifacetest) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/kmod) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/mount) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/policy) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/seccomp) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/systemd) = %{version}-%{release} +Provides: golang(%{import_path}/interfaces/udev) = %{version}-%{release} +Provides: golang(%{import_path}/logger) = %{version}-%{release} +Provides: golang(%{import_path}/osutil) = %{version}-%{release} +Provides: golang(%{import_path}/overlord) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/assertstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/auth) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/cmdstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/configstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/configstate/config) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/devicestate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/hookstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/hookstate/ctlcmd) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/hookstate/hooktest) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/ifacestate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/patch) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/snapstate) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/snapstate/backend) = %{version}-%{release} +Provides: golang(%{import_path}/overlord/state) = %{version}-%{release} +Provides: golang(%{import_path}/partition) = %{version}-%{release} +Provides: golang(%{import_path}/partition/androidbootenv) = %{version}-%{release} +Provides: golang(%{import_path}/partition/grubenv) = %{version}-%{release} +Provides: golang(%{import_path}/partition/ubootenv) = %{version}-%{release} +Provides: golang(%{import_path}/progress) = %{version}-%{release} +Provides: golang(%{import_path}/release) = %{version}-%{release} +Provides: golang(%{import_path}/snap) = %{version}-%{release} +Provides: golang(%{import_path}/snap/snapdir) = %{version}-%{release} +Provides: golang(%{import_path}/snap/snapenv) = %{version}-%{release} +Provides: golang(%{import_path}/snap/snaptest) = %{version}-%{release} +Provides: golang(%{import_path}/snap/squashfs) = %{version}-%{release} +Provides: golang(%{import_path}/store) = %{version}-%{release} +Provides: golang(%{import_path}/strutil) = %{version}-%{release} +Provides: golang(%{import_path}/systemd) = %{version}-%{release} +Provides: golang(%{import_path}/tests/lib/fakestore/refresh) = %{version}-%{release} +Provides: golang(%{import_path}/tests/lib/fakestore/store) = %{version}-%{release} +Provides: golang(%{import_path}/testutil) = %{version}-%{release} +Provides: golang(%{import_path}/timeout) = %{version}-%{release} +Provides: golang(%{import_path}/timeutil) = %{version}-%{release} +Provides: golang(%{import_path}/wrappers) = %{version}-%{release} +Provides: golang(%{import_path}/x11) = %{version}-%{release} + + +%description devel +%{summary} + +This package contains library source intended for +building other packages which use import path with +%{import_path} prefix. +%endif + +%if 0%{?with_unit_test} && 0%{?with_devel} +%package unit-test-devel +Summary: Unit tests for %{name} package + +%if 0%{?with_check} +#Here comes all BuildRequires: PACKAGE the unit tests +#in %%check section need for running +%endif + +%if 0%{?with_check} && ! 0%{?with_bundled} +BuildRequires: golang(github.com/mvo5/goconfigparser) +%endif + +%if ! 0%{?with_bundled} +Requires: golang(github.com/mvo5/goconfigparser) +%else +Provides: bundled(golang(github.com/mvo5/goconfigparser)) +%endif + +# test subpackage tests code from devel subpackage +Requires: %{name}-devel = %{version}-%{release} + +%description unit-test-devel +%{summary} + +This package contains unit tests for project +providing packages with %{import_path} prefix. +%endif + +%prep +%setup -q + +%if ! 0%{?with_bundled} +# Ensure there's no bundled stuff accidentally leaking in... +rm -rf vendor/* + +# XXX: HACK: Fake that we have the right import path because bad testing +# did not verify that this path was actually valid on all supported systems. +mkdir -p vendor/gopkg.in/cheggaaa +ln -s %{gopath}/src/github.com/cheggaaa/pb vendor/gopkg.in/cheggaaa/pb.v1 + +%endif + +%build +# Generate version files +./mkversion.sh "%{version}-%{release}" + +# Build snapd +mkdir -p src/github.com/snapcore +ln -s ../../../ src/github.com/snapcore/snapd + +%if ! 0%{?with_bundled} +export GOPATH=$(pwd):%{gopath} +%else +export GOPATH=$(pwd):$(pwd)/Godeps/_workspace:%{gopath} +%endif + +GOFLAGS= +%if 0%{?with_test_keys} +GOFLAGS="$GOFLAGS -tags withtestkeys" +%endif + +# We have to build snapd first to prevent the build from +# building various things from the tree without additional +# set tags. +%gobuild -o bin/snapd $GOFLAGS %{import_path}/cmd/snapd +%gobuild -o bin/snap $GOFLAGS %{import_path}/cmd/snap +%gobuild -o bin/snapctl $GOFLAGS %{import_path}/cmd/snapctl +# build snap-exec and snap-update-ns completely static for base snaps +CGO_ENABLED=0 %gobuild -o bin/snap-exec $GOFLAGS %{import_path}/cmd/snap-exec +%gobuild -o bin/snap-update-ns --ldflags '-extldflags "-static"' $GOFLAGS %{import_path}/cmd/snap-update-ns + +# We don't need mvo5 fork for seccomp, as we have seccomp 2.3.x +sed -e "s:github.com/mvo5/libseccomp-golang:github.com/seccomp/libseccomp-golang:g" -i cmd/snap-seccomp/*.go +%gobuild -o bin/snap-seccomp $GOFLAGS %{import_path}/cmd/snap-seccomp + +# Build SELinux module +pushd ./data/selinux +make SHARE="%{_datadir}" TARGETS="snappy" +popd + +# Build snap-confine +pushd ./cmd +# FIXME This is a hack to get rid of a patch we have to ship for the +# Fedora package at the moment as /usr/lib/rpm/redhat/redhat-hardened-ld +# accidentially adds -pie for static executables. See +# https://bugzilla.redhat.com/show_bug.cgi?id=1343892 for a few more +# details. To prevent this from happening we drop the linker +# script and define our LDFLAGS manually for now. +export LDFLAGS="-Wl,-z,relro -z now" +autoreconf --force --install --verbose +# selinux support is not yet available, for now just disable apparmor +# FIXME: add --enable-caps-over-setuid as soon as possible (setuid discouraged!) +%configure \ + --disable-apparmor \ + --libexecdir=%{_libexecdir}/snapd/ \ + --with-snap-mount-dir=%{_sharedstatedir}/snapd/snap \ + --with-merged-usr + +%make_build +popd + +# Build systemd units +pushd ./data/ +make BINDIR="%{_bindir}" LIBEXECDIR="%{_libexecdir}" \ + SYSTEMDSYSTEMUNITDIR="%{_unitdir}" \ + SNAP_MOUNT_DIR="%{_sharedstatedir}/snapd/snap" \ + SNAPD_ENVIRONMENT_FILE="%{_sysconfdir}/sysconfig/snapd" +popd + +# Build environ-tweaking snippet +make -C data/env SNAP_MOUNT_DIR="%{_sharedstatedir}/snapd/snap" + +%install +install -d -p %{buildroot}%{_bindir} +install -d -p %{buildroot}%{_libexecdir}/snapd +install -d -p %{buildroot}%{_mandir}/man1 +install -d -p %{buildroot}%{_unitdir} +install -d -p %{buildroot}%{_sysconfdir}/profile.d +install -d -p %{buildroot}%{_sysconfdir}/sysconfig +install -d -p %{buildroot}%{_sharedstatedir}/snapd/assertions +install -d -p %{buildroot}%{_sharedstatedir}/snapd/desktop/applications +install -d -p %{buildroot}%{_sharedstatedir}/snapd/device +install -d -p %{buildroot}%{_sharedstatedir}/snapd/hostfs +install -d -p %{buildroot}%{_sharedstatedir}/snapd/mount +install -d -p %{buildroot}%{_sharedstatedir}/snapd/seccomp/bpf +install -d -p %{buildroot}%{_sharedstatedir}/snapd/snaps +install -d -p %{buildroot}%{_sharedstatedir}/snapd/snap/bin +install -d -p %{buildroot}%{_localstatedir}/snap +install -d -p %{buildroot}%{_localstatedir}/cache/snapd +install -d -p %{buildroot}%{_datadir}/selinux/devel/include/contrib +install -d -p %{buildroot}%{_datadir}/selinux/packages + +# Install snap and snapd +install -p -m 0755 bin/snap %{buildroot}%{_bindir} +install -p -m 0755 bin/snap-exec %{buildroot}%{_libexecdir}/snapd +install -p -m 0755 bin/snapctl %{buildroot}%{_bindir}/snapctl +install -p -m 0755 bin/snapd %{buildroot}%{_libexecdir}/snapd +install -p -m 0755 bin/snap-update-ns %{buildroot}%{_libexecdir}/snapd +install -p -m 0755 bin/snap-seccomp %{buildroot}%{_libexecdir}/snapd + +# Install SELinux module +install -p -m 0644 data/selinux/snappy.if %{buildroot}%{_datadir}/selinux/devel/include/contrib +install -p -m 0644 data/selinux/snappy.pp.bz2 %{buildroot}%{_datadir}/selinux/packages + +# Install snap(1) man page +bin/snap help --man > %{buildroot}%{_mandir}/man1/snap.1 + +# Install the "info" data file with snapd version +install -m 644 -D data/info %{buildroot}%{_libexecdir}/snapd/info + +# Install bash completion for "snap" +install -m 644 -D data/completion/snap %{buildroot}%{_datadir}/bash-completion/completions/snap +install -m 644 -D data/completion/complete.sh %{buildroot}%{_libexecdir}/snapd +install -m 644 -D data/completion/etelpmoc.sh %{buildroot}%{_libexecdir}/snapd + +# Install snap-confine +pushd ./cmd +%make_install +# Undo the 0000 permissions, they are restored in the files section +chmod 0755 %{buildroot}%{_sharedstatedir}/snapd/void +# We don't use AppArmor +rm -rfv %{buildroot}%{_sysconfdir}/apparmor.d +# ubuntu-core-launcher is dead +rm -fv %{buildroot}%{_bindir}/ubuntu-core-launcher +popd + +# Install all systemd units +pushd ./data/ +%make_install SYSTEMDSYSTEMUNITDIR="%{_unitdir}" BINDIR="%{_bindir}" LIBEXECDIR="%{_libexecdir}" +# Remove snappy core specific units +rm -fv %{buildroot}%{_unitdir}/snapd.system-shutdown.service +rm -fv %{buildroot}%{_unitdir}/snapd.snap-repair.* +rm -fv %{buildroot}%{_unitdir}/snapd.core-fixup.* +popd + +# Remove snappy core specific scripts +rm %{buildroot}%{_libexecdir}/snapd/snapd.core-fixup.sh + +# Install environ-tweaking snippet +pushd ./data/env +%make_install +popd + +# Disable re-exec by default +echo 'SNAP_REEXEC=0' > %{buildroot}%{_sysconfdir}/sysconfig/snapd + +# Install snap management script +install -pm 0755 packaging/fedora/snap-mgmt.sh %{buildroot}%{_libexecdir}/snapd/snap-mgmt + +# Create state.json file to be ghosted +touch %{buildroot}%{_sharedstatedir}/snapd/state.json + +# source codes for building projects +%if 0%{?with_devel} +install -d -p %{buildroot}/%{gopath}/src/%{import_path}/ +echo "%%dir %%{gopath}/src/%%{import_path}/." >> devel.file-list +# find all *.go but no *_test.go files and generate devel.file-list +for file in $(find . -iname "*.go" -o -iname "*.s" \! -iname "*_test.go") ; do + echo "%%dir %%{gopath}/src/%%{import_path}/$(dirname $file)" >> devel.file-list + install -d -p %{buildroot}/%{gopath}/src/%{import_path}/$(dirname $file) + cp -pav $file %{buildroot}/%{gopath}/src/%{import_path}/$file + echo "%%{gopath}/src/%%{import_path}/$file" >> devel.file-list +done +%endif + +# testing files for this project +%if 0%{?with_unit_test} && 0%{?with_devel} +install -d -p %{buildroot}/%{gopath}/src/%{import_path}/ +# find all *_test.go files and generate unit-test.file-list +for file in $(find . -iname "*_test.go"); do + echo "%%dir %%{gopath}/src/%%{import_path}/$(dirname $file)" >> devel.file-list + install -d -p %{buildroot}/%{gopath}/src/%{import_path}/$(dirname $file) + cp -pav $file %{buildroot}/%{gopath}/src/%{import_path}/$file + echo "%%{gopath}/src/%%{import_path}/$file" >> unit-test-devel.file-list +done + +# Install additional testdata +install -d %{buildroot}/%{gopath}/src/%{import_path}/cmd/snap/test-data/ +cp -pav cmd/snap/test-data/* %{buildroot}/%{gopath}/src/%{import_path}/cmd/snap/test-data/ +echo "%%{gopath}/src/%%{import_path}/cmd/snap/test-data" >> unit-test-devel.file-list +%endif + +%if 0%{?with_devel} +sort -u -o devel.file-list devel.file-list +%endif + +%check +# snapd tests +%if 0%{?with_check} && 0%{?with_unit_test} && 0%{?with_devel} +%if ! 0%{?with_bundled} +export GOPATH=%{buildroot}/%{gopath}:%{gopath} +%else +export GOPATH=%{buildroot}/%{gopath}:$(pwd)/Godeps/_workspace:%{gopath} +%endif +%gotest %{import_path}/... +%endif + +# snap-confine tests (these always run!) +pushd ./cmd +make check +popd + +%files +#define license tag if not already defined +%{!?_licensedir:%global license %doc} +%license COPYING +%doc README.md docs/* +%{_bindir}/snap +%{_bindir}/snapctl +%dir %{_libexecdir}/snapd +%{_libexecdir}/snapd/snapd +%{_libexecdir}/snapd/snap-exec +%{_libexecdir}/snapd/info +%{_libexecdir}/snapd/snap-mgmt +%{_mandir}/man1/snap.1* +%{_datadir}/bash-completion/completions/snap +%{_libexecdir}/snapd/complete.sh +%{_libexecdir}/snapd/etelpmoc.sh +%{_sysconfdir}/profile.d/snapd.sh +%{_unitdir}/snapd.socket +%{_unitdir}/snapd.service +%{_unitdir}/snapd.autoimport.service +%{_unitdir}/snapd.refresh.service +%{_unitdir}/snapd.refresh.timer +%config(noreplace) %{_sysconfdir}/sysconfig/snapd +%dir %{_sharedstatedir}/snapd +%dir %{_sharedstatedir}/snapd/assertions +%dir %{_sharedstatedir}/snapd/desktop +%dir %{_sharedstatedir}/snapd/desktop/applications +%dir %{_sharedstatedir}/snapd/device +%dir %{_sharedstatedir}/snapd/hostfs +%dir %{_sharedstatedir}/snapd/mount +%dir %{_sharedstatedir}/snapd/seccomp +%dir %{_sharedstatedir}/snapd/seccomp/bpf +%dir %{_sharedstatedir}/snapd/snaps +%dir %{_sharedstatedir}/snapd/snap +%dir /var/cache/snapd +%ghost %dir %{_sharedstatedir}/snapd/snap/bin +%dir %{_localstatedir}/snap +%ghost %{_sharedstatedir}/snapd/state.json +%{_datadir}/dbus-1/services/io.snapcraft.Launcher.service + +%files -n snap-confine +%doc cmd/snap-confine/PORTING +%license COPYING +%dir %{_libexecdir}/snapd +# For now, we can't use caps +# FIXME: Switch to "%%attr(0755,root,root) %%caps(cap_sys_admin=pe)" asap! +%attr(4755,root,root) %{_libexecdir}/snapd/snap-confine +%{_libexecdir}/snapd/snap-discard-ns +%{_libexecdir}/snapd/snap-seccomp +%{_libexecdir}/snapd/snap-update-ns +%{_libexecdir}/snapd/system-shutdown +%{_mandir}/man1/snap-confine.1* +%{_mandir}/man5/snap-discard-ns.5* +%{_prefix}/lib/udev/snappy-app-dev +%{_udevrulesdir}/80-snappy-assign.rules +%attr(0000,root,root) %{_sharedstatedir}/snapd/void + + +%files selinux +%license data/selinux/COPYING +%doc data/selinux/README.md +%{_datadir}/selinux/packages/snappy.pp.bz2 +%{_datadir}/selinux/devel/include/contrib/snappy.if + +%if 0%{?with_devel} +%files devel -f devel.file-list +%license COPYING +%doc README.md +%dir %{gopath}/src/%{provider}.%{provider_tld}/%{project} +%endif + +%if 0%{?with_unit_test} && 0%{?with_devel} +%files unit-test-devel -f unit-test-devel.file-list +%license COPYING +%doc README.md +%endif + +%post +%systemd_post %{snappy_svcs} +# If install, test if snapd socket and timer are enabled. +# If enabled, then attempt to start them. This will silently fail +# in chroots or other environments where services aren't expected +# to be started. +if [ $1 -eq 1 ] ; then + if systemctl -q is-enabled snapd.socket > /dev/null 2>&1 ; then + systemctl start snapd.socket > /dev/null 2>&1 || : + fi + if systemctl -q is-enabled snapd.refresh.timer > /dev/null 2>&1 ; then + systemctl start snapd.refresh.timer > /dev/null 2>&1 || : + fi +fi + +%preun +%systemd_preun %{snappy_svcs} + +# Remove all Snappy content if snapd is being fully uninstalled +if [ $1 -eq 0 ]; then + %{_libexecdir}/snapd/snap-mgmt --purge || : +fi + + +%postun +%systemd_postun_with_restart %{snappy_svcs} + +%pre selinux +%selinux_relabel_pre + +%post selinux +%selinux_modules_install %{_datadir}/selinux/packages/snappy.pp.bz2 +%selinux_relabel_post + +%postun selinux +%selinux_modules_uninstall snappy +if [ $1 -eq 0 ]; then + %selinux_relabel_post +fi + + +%changelog +* Thu Nov 09 2017 Michael Vogt +- New upstream release 2.29.3 + - daemon: cherry-picked /v2/logs fixes + - cmd/snap-confine: Respect biarch nature of libdirs + - cmd/snap-confine: Ensure snap-confine is allowed to access os- + release + - interfaces: fix udev tagging for hooks + - cmd: fix re-exec bug with classic confinement for host snapd + - tests: disable xdg-open-compat test + - cmd/snap-confine: add slave PTYs and let devpts newinstance + perform mediation + - interfaces/many: misc policy updates for browser-support, cups- + control and network-status + - interfaces/raw-usb: match on SUBSYSTEM, not SUBSYSTEMS + - tests: fix security-device-cgroup* tests on devices with + framebuffer + +* Fri Nov 03 2017 Michael Vogt +- New upstream release 2.29.2 + - snapctl: disable stop/start/restart (2.29) + - cmd/snap-update-ns: fix collection of changes made + +* Fri Nov 03 2017 Michael Vogt +- New upstream release 2.29.1 + - interfaces: fix incorrect signature of ofono DBusPermanentSlot + - interfaces/serial-port: udev tag plugged slots that have just + 'path' via KERNEL + - interfaces/hidraw: udev tag plugged slots that have just 'path' + via KERNEL + - interfaces/uhid: unconditionally add existing uhid device to the + device cgroup + - cmd/snap-update-ns: fix mount rules for font sharing + - tests: disable refresh-undo test on trusty for now + - tests: use `snap change --last=install` in snapd-reexec test + - Revert " wrappers: fail install if exec-line cannot be re-written + - interfaces: don't udev tag devmode or classic snaps + - many: make ignore-validation sticky and send the flag with refresh + requests + +* Mon Oct 30 2017 Michael Vogt +- New upstream release 2.29 + - interfaces/many: miscellaneous updates based on feedback from the + field + - snap-confine: allow reading uevents from any where in /sys + - spread: add bionic beaver + - debian: make packaging/ubuntu-14.04/copyright a real file again + - tests: cherry pick the fix for services test into 2.29 + - cmd/snap-update-ns: initialize logger + - hooks/configure: queue service restarts + - snap-{confine,seccomp}: make @unrestricted fully unrestricted + - interfaces: clean system apparmor cache on core device + - debian: do not build static snap-exec on powerpc + - snap-confine: increase sanity_timeout to 6s + - snapctl: cherry pick service commands changes + - cmd/snap: tell translators about arg names and descs req's + - systemd: run all mount units before snapd.service to avoid race + - store: add a test to show auth failures are forwarded by doRequest + - daemon: convert ErrInvalidCredentials to a 401 Unauthorized error. + - store: forward on INVALID_CREDENTIALS error as + ErrInvalidCredentials + - daemon: generate a forbidden response message if polkit dialog is + dismissed + - daemon: Allow Polkit authorization to cancel changes. + - travis: switch to container based test runs + - interfaces: reduce duplicated code in interface tests mocks + - tests: improve revert related testing + - interfaces: sanitize plugs and slots early in ReadInfo + - store: add download caching + - preserve TMPDIR and HOSTALIASES across snap-confine invocation + - snap-confine: init all arrays with `= {0,}` + - tests: adding test for network-manager interface + - interfaces/mount: don't generate legacy per-hook/per-app mount + profiles + - snap: introduce structured epochs + - tests: fix interfaces-cups-control test for cups-2.2.5 + - snap-confine: cleanup incorrectly created nvidia udev tags + - cmd/snap-confine: update valid security tag regexp + - cmd/libsnap: enable two stranded tests + - cmd,packaging: enable apparmor on openSUSE + - overlord/ifacestate: refresh all security backends on startup + - interfaces/dbus: drop unneeded check for + release.ReleaseInfo.ForceDevMode + - dbus: ensure io.snapcraft.Launcher.service is created on re- + exec + - overlord/auth: continue for now supporting UBUNTU_STORE_ID if the + model is generic-classic + - snap-confine: add support for handling /dev/nvidia-modeset + - interfaces/network-control: remove incorrect rules for tun + - spread: allow setting SPREAD_DEBUG_EACH=0 to disable debug-each + section + - packaging: remove .mnt files on removal + - tests: fix econnreset scenario when the iptables rule was not + created + - tests: add test for lxd interface + - run-checks: use nakedret static checker to check for naked + returns on long functions + - progress: be more flexible in testing ansimeter + - interfaces: fix udev rules for tun + - many: implement our own ANSI-escape-using progress indicator + - snap-exec: update tests to follow main_test pattern + - snap: support "command: foo $ENV_STRING" + - packaging: update nvidia configure options + - snap: add new `snap pack` and use in tests + - cmd: correctly name the "Ubuntu" and "Arch" NVIDIA methods + - cmd: add autogen case for solus + - tests: do not use http://canihazip.com/ which appears to be down + - hooks: commands for controlling own services from snapctl + - snap: refactor cmdGet.Execute() + - interfaces/mount: make Change.Perform testable and test it + - interfaces/mount,cmd/snap-update-ns: move change code + - snap-confine: is_running_on_classic_distribution() looks into os- + release + - interfaces: misc updates for default, browser-support, home and + system-observe + - interfaces: deny lttng by default + - interfaces/lxd: lxd slot implementation can also be an app snap + - release,cmd,dirs: Redo the distro checks to take into account + distribution families + - cmd/snap: completion for alias and unalias + - snap-confine: add new SC_CLEANUP and use it + - snap: refrain from running filepath.Base on random strings + - cmd/snap-confine: put processes into freezer hierarchy + - wrappers: fail install if exec-line cannot be re-written + - cmd/snap-seccomp,osutil: make user/group lookup functions public + - snapstate: deal with snap user data in the /root/ directory + - interfaces: Enhance full-confinement support for biarch + distributions + - snap-confine: Only attempt to copy/mount NVIDIA libs when NVIDIA + is used + - packaging/fedora: Add Fedora 26, 27, and Rawhide symlinks + - overlord/snapstate: prefer a smaller corner case for doing the + wrong thing + - cmd/snap-repair: set user agent for snap-repair http requests + - packaging: bring down the delta between 14.04 and 16.04 + - snap-confine: Ensure lib64 biarch directory is respected + - snap-confine: update apparmor rules for fedora based base snaps + - tests: Increase SNAPD_CONFIGURE_HOOK_TIMEOUT to 3 minutes to + install real snaps + - daemon: use client.Snap instead of map[string]interface{} for + snaps. + - hooks: rename refresh hook to post-refresh + - git: make the .gitingore file a bit more targeted + - interfaces/opengl: don't udev tag nvidia devices and use snap- + confine instead + - cmd/snap-{confine,update-ns}: apply mount profiles using snap- + update-ns + - cmd: update "make hack" + - interfaces/system-observe: allow clients to enumerate DBus + connection names + - snap-repair: implement `snap-repair {list,show}` + - dirs,interfaces: create snap-confine.d on demand when re-executing + - snap-confine: fix base snaps on core + - cmd/snap-repair: fix tests when running as root + - interfaces: add Connection type + - cmd/snap-repair: skip disabled repairs + - cmd/snap-repair: prefer leaking unmanaged fds on test failure over + closing random ones + - snap-repair: make `repair` binary available for repair scripts + - snap-repair: fix missing Close() in TestStatusHappy + - cmd/snap-confine,packaging: import snapd-generated policy + - cmd/snap: return empty document if snap has no configuration + - snap-seccomp: run secondary-arch tests via gcc-multilib + - snap: implement `snap {repair,repairs}` and pass-through to snap- + repair + - interfaces/builtin: allow receiving dbus messages + - snap-repair: implement `snap-repair {done,skip,retry}` + - data/completion: small tweak to snap completion snippet + - dirs: fix classic support detection + - cmd/snap-repair: integrate root public keys for repairs + - tests: fix ubuntu core services + - tests: add new test that checks that the compat snapd-xdg-open + works + - snap-confine: improve error message if core/u-core cannot be found + - tests: only run tests/regression/nmcli on amd64 + - interfaces: mount host system fonts in desktop interface + - interfaces: enable partial apparmor support + - snapstate: auto-install missing base snaps + - spread: work around temporary packaging issue in debian sid + - asserts,cmd/snap-repair: introduce a mandatory summary for repairs + - asserts,cmd/snap-repair: represent RepairID internally as an int + - tests: test the real "xdg-open" from the core snap + - many: implement fetching sections and package names periodically. + - interfaces/network: allow using netcat as client + - snap-seccomp, osutil: use osutil.AtomicFile in snap-seccomp + - snap-seccomp: skip mknod syscall on arm64 + - tests: add trivial canonical-livepatch test + - tests: add test that ensures that all core services are working + - many: add logger.MockLogger() and use it in the tests + - snap-repair: fix test failure in TestRepairHitsTimeout + - asserts: add empty values check in HeadersFromPrimaryKey + - daemon: remove unused installSnap var in test + - daemon: reach for Overlord.Loop less thanks to overlord.Mock + - snap-seccomp: manually resolve socket() call in tests + - tests: change regex used to validate installed ubuntu core snap + - cmd/snapctl: allow snapctl -h without a context (regression fix). + - many: use snapcore/snapd/i18n instead of i18n/dumb + - many: introduce asserts.NotFoundError replacing both ErrNotFound + and store.AssertionNotFoundError + - packaging: don't include any marcos in comments + - overlord: use overlord.Mock in more tests, make sure we check the + outcome of Settle + - tests: try to fix staging tests + - store: simplify api base url config + - systemd: add systemd.MockJournalctl() + - many: provide systemd.MockSystemctl() helper + - tests: improve the listing test to not fail for e.g. 2.28~rc2 + - snapstate: give snapmgrTestSuite.settle() more time to settle + - tests: fix regex to check core version on snap list + - debian: update trusted account-keys check on 14.04 packaging + - interfaces: add udev netlink support to hardware-observe + - overlord: introduce Mock which enables to use Overlord.Settle for + settle in many more places + - snap-repair: execute the repair and capture logs/status + - tests: run the tests/unit/go everywhere + - daemon, snapstate: move ensureCore from daemon/api.go into + snapstate.go + - cmd/snap: get keys or root document + - spread.yaml: turn suse to manual given that it's breaking master + - many: configure store from state, reconfigure store at runtime + - osutil: AtomicWriter (an io.Writer), and io.Reader versions of + AtomicWrite* + - tests: check for negative syscalls in runBpf() and skip those + tests + - docs: use abolute path in PULL_REQUEST_TEMPLATE.md + - store: move device auth endpoint uris to config (#3831) + +* Fri Oct 13 2017 Michael Vogt +- New upstream release 2.28.5 + - snap-confine: cleanup broken nvidia udev tags + - cmd/snap-confine: update valid security tag regexp + - overlord/ifacestate: refresh udev backend on startup + - dbus: ensure io.snapcraft.Launcher.service is created on re- + exec + - snap-confine: add support for handling /dev/nvidia-modeset + - interfaces/network-control: remove incorrect rules for tun + +* Wed Oct 11 2017 Michael Vogt +- New upstream release 2.28.4 + - interfaces/opengl: don't udev tag nvidia devices and use snap- + confine instead + - debian: fix replaces/breaks for snap-xdg-open (thanks to apw!) + +* Wed Oct 11 2017 Michael Vogt +- New upstream release 2.28.3 + - interfaces/lxd: lxd slot implementation can also be an app + snap + +* Tue Oct 10 2017 Michael Vogt +- New upstream release 2.28.2 + - interfaces: fix udev rules for tun + - release,cmd,dirs: Redo the distro checks to take into account + distribution families + +* Wed Sep 27 2017 Michael Vogt +- New upstream release 2.28.1 + - snap-confine: update apparmor rules for fedora based basesnaps + - snapstate: rename refresh hook to post-refresh for consistency + +* Mon Sep 25 2017 Michael Vogt +- New upstream release 2.28 + - hooks: rename refresh to after-refresh + - snap-confine: bind mount /usr/lib/snapd relative to snap-confine + - cmd,dirs: treat "liri" the same way as "arch" + - snap-confine: fix base snaps on core + - hooks: substitute env vars when executing hooks + - interfaces: updates for default, browser-support, desktop, opengl, + upower and stub-resolv.conf + - cmd,dirs: treat manjaro the same as arch + - systemd: do not run auto-import and repair services on classic + - packaging/fedora: Ensure vendor/ is empty for builds and fix spec + to build current master + - many: fix TestSetConfNumber missing an Unlock and other fragility + improvements + - osutil: adjust StreamCommand tests for golang 1.9 + - daemon: allow polkit authorisation to install/remove snaps + - tests: make TestCmdWatch more robust + - debian: improve package description + - interfaces: add netlink kobject uevent to hardware observe + - debian: update trusted account-keys check on 14.04 packaging + - interfaces/network-{control,observe}: allow receiving + kobject_uevent() messages + - tests: fix lxd test for external backend + - snap-confine,snap-update-ns: add -no-pie to fix FTBFS on + go1.7,ppc64 + - corecfg: mock "systemctl" in all corecfg tests + - tests: fix unit tests on Ubuntu 14.04 + - debian: add missing flags when building static snap-exec + - many: end-to-end support for the bare base snap + - overlord/snapstate: SetRootDir from SetUpTest, not in just some + tests + - store: have an ad-hoc method on cfg to get its list of uris for + tests + - daemon: let client decide whether to allow interactive auth via + polkit + - client,daemon,snap,store: add license field + - overlord/snapstate: rename HasCurrent to IsInstalled, remove + superfluous/misleading check from All + - cmd/snap: SetRootDir from SetUpTest, not in just some individual + tests. + - systemd: rename snap-repair.{service,timer} to snapd.snap- + repair.{service,timer} + - snap-seccomp: remove use of x/net/bpf from tests + - httputil: more naive per go version way to recreate a default + transport for tls reconfig + - cmd/snap-seccomp/main_test.go: add one more syscall for arm64 + - interfaces/opengl: use == to compare, not = + - cmd/snap-seccomp/main_test.go: add syscalls for armhf and arm64 + - cmd/snap-repair: track and use a lower bound for the time for + TLS checks + - interfaces: expose bluez interface on classic OS + - snap-seccomp: add in-kernel bpf tests + - overlord: always try to get a serial, lazily on classic + - tests: add nmcli regression test + - tests: deal with __PNR_chown on aarch64 to fix FTBFS on arm64 + - tests: add autopilot-introspection interface test + - vendor: fix artifact from manually editing vendor/vendor.json + - tests: rename complexion to test-snapd-complexion + - interfaces: add desktop and desktop-legacy + interfaces/desktop: add new 'desktop' interface for modern DEs* + interfaces/builtin/desktop_test.go: use modern testing techniques* + interfaces/wayland: allow read on /etc/drirc for Plasma desktop* + interfaces/desktop-legacy: add new 'legacy' interface (currently + for a11y and input) + - tests: fix race in snap userd test + - devices/iio: add read/write for missing sysfs entries + - spread: don't set HTTPS?_PROXY for linode + - cmd/snap-repair: check signatures of repairs from Next + - env: set XDG_DATA_DIRS for wayland et.al. + - interfaces/{default,account-control}: Use username/group instead + of uid/gid + - interfaces/builtin: use udev tagging more broadly + - tests: add basic lxd test + - wrappers: ensure bash completion snaps install on core + - vendor: use old golang.org/x/crypto/ssh/terminal to build on + powerpc again + - docs: add PULL_REQUEST_TEMPLATE.md + - interfaces: fix network-manager plug + - hooks: do not error out when hook is optional and no hook handler + is registered + - cmd/snap: add userd command to replace snapd-xdg-open + - tests: new regex used to validate the core version on extra snaps + ass... + - snap: add new `snap switch` command + - tests: wait more and more debug info about fakestore start issues + - apparmor,release: add better apparmor detection/mocking code + - interfaces/i2c: adjust sysfs rule for alternate paths + - interfaces/apparmor: add missing call to dirs.SetRootDir + - cmd: "make hack" now also installs snap-update-ns + - tests: copy files with less verbosity + - cmd/snap-confine: allow using additional libraries required by + openSUSE + - packaging/fedora: Merge changes from Fedora Dist-Git + - snapstate: improve the error message when classic confinement is + not supported + - tests: add test to ensure amd64 can run i386 syscall binaries + - tests: adding extra info for fakestore when fails to start + - tests: install most important snaps + - cmd/snap-repair: more test coverage of filtering + - squashfs: remove runCommand/runCommandWithOutput as we do not need + it + - cmd/snap-repair: ignore superseded revisions, filter on arch and + models + - hooks: support for refresh hook + - Partial revert "overlord/devicestate, store: update device auth + endpoints URLs" + - cmd/snap-confine: allow reading /proc/filesystems + - cmd/snap-confine: genearlize apparmor profile for various lib + layout + - corecfg: fix proxy.* writing and add integration test + - corecfg: deal with system.power-key-action="" correctly + - vendor: update vendor.json after (presumed) manual edits + - cmd/snap: in `snap info`, don't print a newline between tracks + - daemon: add polkit support to /v2/login + - snapd,snapctl: decode json using Number + - client: fix go vet 1.7 errors + - tests: make 17.04 shellcheck clean + - tests: remove TestInterfacesHelp as it breaks when go-flags + changes + - snapstate: undo a daemon restart on classic if needed + - cmd/snap-repair: recover brand/model from + /var/lib/snapd/seed/assertions checking signatures and brand + account + - spread: opt into unsafe IO during spread tests + - snap-repair: update snap-repair/runner_test.go for API change in + makeMockServer + - cmd/snap-repair: skeleton code around actually running a repair + - tests: wait until the port is listening after start the fake store + - corecfg: fix typo in tests + - cmd/snap-repair: test that redirects works during fetching + - osutil: honor SNAPD_UNSAFE_IO for testing + - vendor: explode and make more precise our golang.go/x/crypto deps, + use same version as Debian unstable + - many: sanitize NewStoreStack signature, have shared default store + test private keys + - systemd: disable `Nice=-5` to fix error when running inside lxd + - spread.yaml: update delta ref to 2.27 + - cmd/snap-repair: use E-Tags when refetching a repair to retry + - interfaces/many: updates based on chromium and mrrescue denials + - cmd/snap-repair: implement most logic to get the next repair to + run/retry in a brand sequence + - asserts/assertstest: copy headers in SigningDB.Sign + - interfaces: convert uhid to common interface and test cases + improvement for time_control and opengl + - many tests: move all panicing fake store methods to a common place + - asserts: add store assertion type + - interfaces: don't crash if content slot has no attributes + - debian: do not build with -buildmode=pie on i386 + - wrappers: symlink completion snippets when symlinking binaries + - tests: adding more debug information for the interfaces-cups- + control … + - apparmor: pass --quiet to parser on load unless SNAPD_DEBUG is set + - many: allow and support serials signed by the 'generic' authority + instead of the brand + - corecfg: add proxy configuration via `snap set core + proxy.{http,https,ftp}=...` + - interfaces: a bunch of interfaces test improvement + - tests: enable regression and completion suites for opensuse + - tests: installing snapd for nested test suite + - interfaces: convert lxd_support to common iface + - interfaces: add missing test for camera interface. + - snap: add support for parsing snap layout section + - cmd/snap-repair: like for downloads we cannot have a timeout (at + least for now), less aggressive retry strategies + - overlord: rely on more conservative ensure interval + - overlord,store: no piles of return args for methods gathering + device session request params + - overlord,store: send model assertion when setting up device + sessions + - interfaces/misc: updates for unity7/x11, browser- + support, network-control and mount-observe + interfaces/unity7,x11: update for NETLINK_KOBJECT_UEVENT + interfaces/browser-support: update sysfs reads for + newer browser versions, interfaces/network-control: rw for + ieee80211 advanced wireless interfaces/mount-observe: allow read + on sysfs entries for block devices + - tests: use dnf --refresh install to avert stale cache + - osutil: ensure TestLockUnlockWorks uses supported flock + - interfaces: convert lxd to common iface + - tests: restart snapd to ensure re-exec settings are applied + - tests: fix interfaces-cups-control test + - interfaces: improve and tweak bunch of interfaces test cases. + - tests: adding extra worker for fedora + - asserts,overlord/devicestate: support predefined assertions that + don't establish foundational trust + - interfaces: convert two hardware_random interfaces to common iface + - interfaces: convert io_ports_control to common iface + - tests: fix for upgrade test on fedora + - daemon, client, cmd/snap: implement snap start/stop/restart + - cmd/snap-confine: set _FILE_OFFSET_BITS to 64 + - interfaces: covert framebuffer to commonInterface + - interfaces: convert joystick to common iface + - interfaces/builtin: add the spi interface + - wrappers, overlord/snapstate/backend: make link-snap clean up on + failure. + - interfaces/wayland: add wayland interface + - interfaces: convert kvm to common iface + - tests: extend upower-observe test to cover snaps providing slots + - tests: enable main suite for opensuse + - interfaces: convert physical_memory_observe to common iface + - interfaces: add missing test for optical_drive interface. + - interfaces: convert physical_memory_control to common iface + - interfaces: convert ppp to common iface + - interfaces: convert time-control to common iface + - tests: fix failover test + - interfaces/builtin: rework for avahi interface + - interfaces: convert broadcom-asic-control to common iface + - snap/snapenv: document the use of CoreSnapMountDir for SNAP + - packaging/arch: drop patches merged into master + - cmd: fix mustUnsetenv docstring (thanks to Chipaca) + - release: remove default from VERSION_ID + - tests: enable regression, upgrade and completion test suites for + fedora + - tests: restore interfaces-account-control properly + - overlord/devicestate, store: update device auth endpoints URLs + - tests: fix install-hook test failure + - tests: download core and ubuntu-core at most once + - interfaces: add common support for udev + - overlord/devicestate: fix, don't assume that the serial is backed + by a 1-key chain + - cmd/snap-confine: don't share /etc/nsswitch from host + - store: do not resume a download when we already have the whole + thing + - many: implement "snap logs" + - store: don't call useDeltas() twice in quick succession + - interfaces/builtin: add kvm interface + - snap/snapenv: always expect /snap for $SNAP + - cmd: mark arch as non-reexecing distro + - cmd: fix tests that assume /snap mount + - gitignore: ignore more build artefacts + - packaging: add current arch packaging + - interfaces/unity7: allow receiving media key events in (at least) + gnome-shell + - interfaces/many, cmd/snap-confine: miscellaneous policy updates + - interfaces/builtin: implement broadcom-asic-control interface + - interfaces/builtin: reduce duplication and remove cruft in + Sanitize{Plug,Slot} + - tests: apply underscore convention for SNAPMOUNTDIR variable + - interfaces/greengrass-support: adjust accesses now that have + working snap + - daemon, client, cmd/snap: implement "snap services" + - tests: fix refresh tests not stopping fake store for fedora + - many: add the interface command + - overlord/snapstate/backend: some copydata improvements + - many: support querying and completing assertion type names + - interfaces/builtin: discard empty Validate{Plug,Slot} + - cmd/snap-repair: start of Runner, implement first pass of Peek + and Fetch + - tests: enable main suite on fedora + - snap: do not always quote the snap info summary + - vendor: update go-flags to address crash in "snap debug" + - interfaces: opengl support pci device and vendor + - many: start implenting "base" snap type on the snapd side + - arch,release: map armv6 correctly + - many: expose service status in 'snap info' + - tests: add browser-support interface test + - tests: disable snapd-notify for the external backend + - interfaces: Add /run/uuid/request to openvswitch + - interfaces: add password-manager-service implicit classic + interface + - cmd: rework reexec detection + - cmd: fix re-exec bug when starting from snapd 2.21 + - tests: dependency packages installed during prepare-project + - tests: remove unneeded check for re-exec in InternalToolPath() + - cmd,tests: fix classic confinement confusing re-execution code + - store: configurable base api + - tests: fix how package lists are updated for opensuse and fedora + +* Thu Sep 07 2017 Michael Vogt +- New upstream release 2.27.6 + - interfaces: add udev netlink support to hardware-observe + - interfaces/network-{control,observe}: allow receiving + kobject_uevent() messages + +* Wed Aug 30 2017 Michael Vogt +- New upstream release 2.27.5 + - interfaces: fix network-manager plug regression + - hooks: do not error when hook handler is not registered + - interfaces/alsa,pulseaudio: allow read on udev data for sound + - interfaces/optical-drive: read access to udev data for /dev/scd* + - interfaces/browser-support: read on /proc/vmstat and misc udev + data + +* Thu Aug 24 2017 Michael Vogt +- New upstream release 2.27.4 + - snap-seccomp: add secondary arch for unrestricted snaps as well + +* Fri Aug 18 2017 Michael Vogt +- New upstream release 2.27.3 + - systemd: disable `Nice=-5` to fix error when running inside lxdSee + https://bugs.launchpad.net/snapd/+bug/1709536 + +* Wed Aug 16 2017 Neal Gompa - 2.27.2-2 +- Bump to rebuild for F27 and Rawhide + +* Wed Aug 16 2017 Neal Gompa - 2.27.2-1 +- Release 2.27.2 to Fedora (RH#1482173) + +* Wed Aug 16 2017 Michael Vogt +- New upstream release 2.27.2 + - tests: remove TestInterfacesHelp as it breaks when go-flags + changes + - interfaces: don't crash if content slot has no attributes + - debian: do not build with -buildmode=pie on i386 + - interfaces: backport broadcom-asic-control interface + - interfaces: allow /usr/bin/xdg-open in unity7 + - store: do not resume a download when we already have the whole + thing + +* Mon Aug 14 2017 Neal Gompa - 2.27.1-1 +- Release 2.27.1 to Fedora (RH#1481247) + +* Mon Aug 14 2017 Michael Vogt +- New upstream release 2.27.1 + - tests: use dnf --refresh install to avert stale cache + - tests: fix test failure on 14.04 due to old version of + flock + - updates for unity7/x11, browser-support, network-control, + mount-observe + - interfaces/unity7,x11: update for NETLINK_KOBJECT_UEVENT + - interfaces/browser-support: update sysfs reads for + newer browser versions + - interfaces/network-control: rw for ieee80211 advanced wireless + - interfaces/mount-observe: allow read on sysfs entries for block + devices + +* Thu Aug 10 2017 Neal Gompa - 2.27-1 +- Release 2.27 to Fedora (RH#1458086) + +* Thu Aug 10 2017 Michael Vogt +- New upstream release 2.27 + - fix build failure on 32bit fedora + - interfaces: add password-manager-service implicit classic interface + - interfaces/greengrass-support: adjust accesses now that have working + snap + - interfaces/many, cmd/snap-confine: miscellaneous policy updates + - interfaces/unity7: allow receiving media key events in (at least) + gnome-shell + - cmd: fix re-exec bug when starting from snapd 2.21 + - tests: restore interfaces-account-control properly + - cmd: fix tests that assume /snap mount + - cmd: mark arch as non-reexecing distro + - snap-confine: don't share /etc/nsswitch from host + - store: talk to api.snapcraft.io for purchases + - hooks: support for install and remove hooks + - packaging: fix Fedora support + - tests: add bluetooth-control interface test + - store: talk to api.snapcraft.io for assertions + - tests: remove snapd before building from branch + - tests: add avahi-observe interface test + - store: orders API now checks if customer is ready + - cmd/snap: snap find only searches stable + - interfaces: updates default, mir, optical-observe, system-observe, + screen-inhibit-control and unity7 + - tests: speedup prepare statement part 1 + - store: do not send empty refresh requests + - asserts: fix error handling in snap-developer consistency check + - systemd: add explicit sync to snapd.core-fixup.sh + - snapd: generate snap cookies on startup + - cmd,client,daemon: expose "force devmode" in sysinfo + - many: introduce and use strutil.ListContains and also + strutil.SortedListContains + - assserts,overlord/assertstate: test we don't accept chains of + assertions founded on a self-signed key coming externally + - interfaces: enable access to bridge settings + - interfaces: fix copy-pasted iio vs io in io-ports-control + - cmd/snap-confine: various small fixes and tweaks to seccomp + support code + - interfaces: bring back seccomp argument filtering + - systemd, osutil: rework systemd logs in preparation for services + commands + - tests: store /etc/systemd/system/snap-*core*.mount in snapd- + state.tar.gz + - tests: shellcheck improvements for tests/main tasks - first set of + tests + - cmd/snap: `--last` for abort and watch, and aliases + (search→find, change→tasks) + - tests: shellcheck improvements for tests/lib scripts + - tests: create ramdisk if it's not present + - tests: shellcheck improvements for nightly upgrade and regressions + tests + - snapd: fix for snapctl get panic on null config values. + - tests: fix for rng-tools service not restarting + - systemd: add snapd.core-fixup.service unit + - cmd: avoid using current symlink in InternalToolPath + - tests: fix timeout issue for test refresh core with hanging … + - intefaces: control bridged vlan/ppoe-tagged traffic + - cmd/snap: include snap type in notes + - overlord/state: Abort() only visits each task once + - tests: extend find-private test to cover more cases + - snap-seccomp: skip socket() tests on systems that use socketcall() + instead of socket() + - many: support snap title as localized/title-cased name + - snap-seccomp: deal with mknod on aarch64 in the seccomp tests + - interfaces: put base policy fragments inside each interface + - asserts: introduce NewDecoderWithTypeMaxBodySize + - tests: fix snapd-notify when it takes more time to restart + - snap-seccomp: fix snap-seccomp tests in artful + - tests: fix for create-key task to avoid rng-tools service ramains + alive + - snap-seccomp: make sure snap-seccomp writes the bpf file + atomically + - tests: do not disable ipv6 on core systems + - arch: the kernel architecture name is armv7l instead of armv7 + - snap-confine: ensure snap-confine waits some seconds for seccomp + security profiles + - tests: shellcheck improvements for tests/nested tasks + - wrappers: add SyslogIdentifier to the service unit files. + - tests: shellcheck improvements for unit tasks + - asserts: implement FindManyTrusted as well + - asserts: open up and optimize Encoder to help avoiding unnecessary + copying + - interfaces: simplify snap-confine by just loading pre-generated + bpf code + - tests: restart rng-tools services after few seconds + - interfaces, tests: add mising dbus abstraction to system-observe + and extend spread test + - store: change main store host to api.snapcraft.io + - overlord/cmdstate: new package for running commands as tasks. + - spread: help libapt resolve installing libudev-dev + - tests: show the IP from .travis.yaml + - tests/main: use pkgdb function in more test cases + - cmd,daemon: add debug command for displaying the base policy + - tests: prevent quoting error on opensuse + - tests: fix nightly suite + - tests: add linode-sru backend + - snap-confine: validate SNAP_NAME against security tag + - tests: fix ipv6 disable for ubuntu-core + - tests: extend core-revert test to cover bluez issues + - interfaces/greengrass-support: add support for Amazon Greengrass + as a snap + - asserts: support timestamp and optional disabled header on repair + - tests: reboot after upgrading to snapd on the -proposed pocket + - many: fix test cases to work with different DistroLibExecDir + - tests: reenable help test on ubuntu and debian systems + - packaging/{opensuse,fedora}: allow package build with testkeys + included + - tests/lib: generalize RPM build support + - interfaces/builtin: sync connected slot and permanent slot snippet + - tests: fix snap create-key by restarting automatically rng-tools + - many: switch to use http numeric statuses as agreed + - debian: add missing Type=notify in 14.04 packaging + - tests: mark interfaces-openvswitch as manual due to prepare errors + - debian: unify built_using between the 14.04 and 16.04 packaging + branch + - tests: pull from urandom when real entropy is not enough + - tests/main/manpages: install missing man package + - tests: add refresh --time output check + - debian: add missing "make -C data/systemd clean" + - tests: fix for upgrade test when it is repeated + - tests/main: use dir abstraction in a few more test cases + - tests/main: check for confinement in a few more interface tests + - spread: add fedora snap bin dir to global PATH + - tests: check that locale-control is not present on core + - many: snapctl outside hooks + - tests: add whoami check + - interfaces: compose the base declaration from interfaces + - tests: fix spread flaky tests linode + - tests,packaging: add package build support for openSUSE + - many: slight improvement of some snap error messaging + - errtracker: Include /etc/apparmor.d/usr.lib.snap-confine md5sum in + err reports + - tests: fix for the test postrm-purge + - tests: restoring the /etc/environment and service units config for + each test + - daemon: make snapd a "Type=notify" daemon and notify when startup + is done + - cmd/snap-confine: add support for --base snap + - many: derive implicit slots from interface meta-data + - tests: add core revert test + - tests,packaging: add package build support for Fedora for our + spread setup + - interfaces: move base declaration to the policy sub-package + - tests: fix for snapd-reexec test cheking for restart info on debug + log + - tests: show available entropy on error + - tests: clean journalctl logs on trusty + - tests: fix econnreset on staging + - tests: modify core before calling set + - tests: add snap-confine privilege test + - tests: add staging snap-id + - interfaces/builtin: silence ptrace denial for network-manager + - tests: add alsa interface spread test + - tests: prefer ipv4 over ipv6 + - tests: fix for econnreset test checking that the download already + started + - httputil,store: extract retry code to httputil, reorg usages + - errtracker: report if snapd did re-execute itself + - errtracker: include bits of snap-confine apparmor profile + - tests: take into account staging snap-ids for snap-info + - cmd: add stub new snap-repair command and add timer + - many: stop "snap refresh $x --channel invalid" from working + - interfaces: revert "interfaces: re-add reverted ioctl and quotactl + - snapstate: consider connect/disconnect tasks in + CheckChangeConflict. + - interfaces: disable "mknod |N" in the default seccomp template + again + - interfaces,overlord/ifacestate: make sure installing slots after + plugs works similarly to plugs after slots + - interfaces/seccomp: add bind() syscall for forced-devmode systems + - packaging/fedora: Sync packaging from Fedora Dist-Git + - tests: move static and unit tests to spread task + - many: error types should be called FooError, not ErrFoo. + - partition: add directory sync to the save uboot.env file code + - cmd: test everything (100% coverage \o/) + - many: make shell scripts shellcheck-clean + - tests: remove additional setup for docker on core + - interfaces: add summary to each interface + - many: remove interface meta-data from list of connections + - logger (& many more, to accommodate): drop explicit syslog. + - packaging: import packaging bits for opensuse + - snapstate,many: implement snap install --unaliased + - tests/lib: abstract build dependency installation a bit more + - interfaces, osutil: move flock code from interfaces/mount to + osutil + - cmd: auto import assertions only from ext4,vfat file systems + - many: refactor in preparation for 'snap start' + - overlord/snapstate: have an explicit code path last-refresh + unset/zero => immediately refresh try + - tests: fixes for executions using the staging store + - tests: use pollinate to seed the rng + - cmd/snap,tests: show the sha3-384 of the snap for snap info + --verbose SNAP-FILE + - asserts: simplify and adjust repair assertion definition + - cmd/snap,tests: show the snap id if available in snap info + - daemon,overlord/auth: store from model assertion wins + - cmd/snap,tests/main: add confinement switch instead of spread + system blacklisting + - many: cleanup MockCommands and don't leave a process around after + hookstate tests + - tests: update listing test to the core version number schema + - interfaces: allow snaps to use the timedatectl utility + - packaging: Add Fedora packaging files + - tests/libs: add distro_auto_remove_packages function + - cmd/snap: correct devmode note for anomalous state + - tests/main/snap-info: use proper pkgdb functions to install distro + packages + - tests/lib: use mktemp instead of tempfile to work cross-distro + - tests: abstract common dirs which differ on distributions + - many: model and expose interface meta-data. + - overlord: make config defaults from gadget work also at first boot + - interfaces/log-observe: allow using journalctl from hostfs for + classic distro + - partition,snap: add support for android boot + - errtracker: small simplification around readMachineID + - snap-confine: move rm_rf_tmp to test-utils. + - tests/lib: introduce pkgdb helper library + - errtracker: try multiple paths to read machine-id + - overlord/hooks: make sure only one hook for given snap is executed + at a time. + - cmd/snap-confine: use SNAP_MOUNT_DIR to setup /snap inside the + confinement env + - tests: bump kill-timeout and remove quiet call on build + - tests/lib/snaps: add a test store snap with a passthrough + configure hook + - daemon: teach the daemon to wait on active connections when + shutting down + - tests: remove unit tests task + - tests/main/completion: source from /usr/share/bash-completion + - assertions: add "repair" assertion + - interfaces/seccomp: document Backend.NewSpecification + - wrappers: make StartSnapServices cleanup any services that were + added if a later one fails + - overlord/snapstate: avoid creating command aliases for daemons + - vendor: remove unused packages + - vendor,partition: fix panics from uenv + - cmd,interfaces/mount: run snap-update-ns and snap-discard-ns from + core if possible + - daemon: do not allow to install ubuntu-core anymore + - wrappers: service start/stop were inconsistent + - tests: fix failing tests (snap core version, syslog changes) + - cmd/snap-update-ns: add actual implementation + - tests: improve entropy also for ubuntu + - cmd/snap-confine: use /etc/ssl from the core snap + - wrappers: don't convert between []byte and string needlessly. + - hooks: default timeout + - overlord/snapstate: Enable() was ignoring the flags from the + snap's state, resulting in losing "devmode" on disable/enable. + - difs,interfaces/mount: add support for locking namespaces + - interfaces/mount: keep track of kept mount entries + - tests/main: move a bunch of greps over to MATCH + - interfaces/builtin: make all interfaces private + - interfaces/mount: spell unmount correctly + - tests: allow 16-X.Y.Z version of core snap + - the timezone_control interface only allows changing /etc/timezone + and /etc/writable/timezone. systemd-timedated also updated the + link of /etc/localtime and /etc/writable/localtime ... allow + access to this file too + - cmd/snap-confine: aggregate operations holding global lock + - api, ifacestate: resolve disconnect early + - interfaces/builtin: ensure we don't register interfaces twice + +* Thu Aug 03 2017 Fedora Release Engineering - 2.26.3-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 2.26.3-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Thu May 25 2017 Neal Gompa - 2.26.3-3 +- Cover even more stuff for proper erasure on final uninstall (RH#1444422) + +* Sun May 21 2017 Neal Gompa - 2.26.3-2 +- Fix error in script for removing Snappy content (RH#1444422) +- Adjust changelog bug references to be specific on origin + +* Wed May 17 2017 Neal Gompa - 2.26.3-1 +- Update to snapd 2.26.3 +- Drop merged and unused patches +- Cover more Snappy content for proper erasure on final uninstall (RH#1444422) +- Add temporary fix to ensure generated seccomp profiles don't break snapctl + +* Mon May 01 2017 Neal Gompa - 2.25-1 +- Update to snapd 2.25 +- Ensure all Snappy content is gone on final uninstall (RH#1444422) + +* Tue Apr 11 2017 Neal Gompa - 2.24-1 +- Update to snapd 2.24 +- Drop merged patches +- Install snap bash completion and snapd info file + +* Wed Apr 05 2017 Neal Gompa - 2.23.6-4 +- Test if snapd socket and timer enabled and start them if enabled on install + +* Sat Apr 01 2017 Neal Gompa - 2.23.6-3 +- Fix profile.d generation so that vars aren't expanded in package build + +* Fri Mar 31 2017 Neal Gompa - 2.23.6-2 +- Fix the overlapping file conflicts between snapd and snap-confine +- Rework package descriptions slightly + +* Thu Mar 30 2017 Neal Gompa - 2.23.6-1 +- Rebase to snapd 2.23.6 +- Rediff patches +- Re-enable seccomp +- Fix building snap-confine on 32-bit arches +- Set ExclusiveArch based on upstream supported arch list + +* Wed Mar 29 2017 Neal Gompa - 2.23.5-1 +- Rebase to snapd 2.23.5 +- Disable seccomp temporarily avoid snap-confine bugs (LP#1674193) +- Use vendorized build for non-Fedora + +* Mon Mar 13 2017 Neal Gompa - 2.23.1-1 +- Rebase to snapd 2.23.1 +- Add support for vendored tarball for non-Fedora targets +- Use merged in SELinux policy module + +* Sat Feb 11 2017 Fedora Release Engineering - 2.16-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Wed Oct 19 2016 Zygmunt Krynicki - 2.16-1 +- New upstream release + +* Tue Oct 18 2016 Neal Gompa - 2.14-2 +- Add SELinux policy module subpackage + +* Tue Aug 30 2016 Zygmunt Krynicki - 2.14-1 +- New upstream release + +* Tue Aug 23 2016 Zygmunt Krynicki - 2.13-1 +- New upstream release + +* Thu Aug 18 2016 Zygmunt Krynicki - 2.12-2 +- Correct license identifier + +* Thu Aug 18 2016 Zygmunt Krynicki - 2.12-1 +- New upstream release + +* Thu Aug 18 2016 Zygmunt Krynicki - 2.11-8 +- Add %%dir entries for various snapd directories +- Tweak Source0 URL + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-7 +- Disable snapd re-exec feature by default + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-6 +- Don't auto-start snapd.socket and snapd.refresh.timer + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-5 +- Don't touch snapd state on removal + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-4 +- Use ExecStartPre to load squashfs.ko before snapd starts +- Use dedicated systemd units for Fedora + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-3 +- Remove systemd preset (will be requested separately according to distribution + standards). + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-2 +- Use Requires: kmod(squashfs.ko) instead of Requires: kernel-modules + +* Tue Aug 16 2016 Zygmunt Krynicki - 2.11-1 +- New upstream release +- Move private executables to /usr/libexec/snapd/ + +* Fri Jun 24 2016 Zygmunt Krynicki - 2.0.9-2 +- Depend on kernel-modules to ensure that squashfs can be loaded. Load it afer + installing the package. This hopefully fixes + https://github.com/zyga/snapcore-fedora/issues/2 + +* Fri Jun 17 2016 Zygmunt Krynicki - 2.0.9 +- New upstream release + https://github.com/snapcore/snapd/releases/tag/2.0.9 + +* Tue Jun 14 2016 Zygmunt Krynicki - 2.0.8.1 +- New upstream release + +* Fri Jun 10 2016 Zygmunt Krynicki - 2.0.8 +- First package for Fedora diff -Nru snapd-2.28.5/packaging/fedora-rawhide/snap-mgmt.sh snapd-2.29.3/packaging/fedora-rawhide/snap-mgmt.sh --- snapd-2.28.5/packaging/fedora-rawhide/snap-mgmt.sh 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/packaging/fedora-rawhide/snap-mgmt.sh 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,137 @@ +#!/bin/bash + +# Overlord management of snapd for package manager actions. +# Implements actions that would be invoked in %pre(un) actions for snapd. +# Derived from the snapd.postrm scriptlet used in the Ubuntu packaging for +# snapd. + +set -e + +SNAP_MOUNT_DIR="/var/lib/snapd/snap" + +show_help() { + exec cat <<'EOF' +Usage: snap-mgmt.sh [OPTIONS] + +A simple script to cleanup snap installations. + +optional arguments: + --help Show this help message and exit + --snap-mount-dir= Provide a path to be used as $SNAP_MOUNT_DIR + --purge Purge all data from $SNAP_MOUNT_DIR +EOF +} + +SNAP_UNIT_PREFIX="$(systemd-escape -p ${SNAP_MOUNT_DIR})" + +systemctl_stop() { + unit="$1" + if systemctl is-active -q "$unit"; then + echo "Stoping $unit" + systemctl stop -q "$unit" || true + fi +} + +purge() { + # undo any bind mount to ${SNAP_MOUNT_DIR} that resulted from LP:#1668659 + if grep -q "${SNAP_MOUNT_DIR} ${SNAP_MOUNT_DIR}" /proc/self/mountinfo; then + umount -l "${SNAP_MOUNT_DIR}" || true + fi + + mounts=$(systemctl list-unit-files --full | grep "^${SNAP_UNIT_PREFIX}[-.].*\.mount" | cut -f1 -d ' ') + services=$(systemctl list-unit-files --full | grep "^${SNAP_UNIT_PREFIX}[-.].*\.service" | cut -f1 -d ' ') + for unit in $services $mounts; do + # ensure its really a snap mount unit or systemd unit + if ! grep -q 'What=/var/lib/snapd/snaps/' "/etc/systemd/system/$unit" && ! grep -q 'X-Snappy=yes' "/etc/systemd/system/$unit"; then + echo "Skipping non-snapd systemd unit $unit" + continue + fi + + echo "Stopping $unit" + systemctl_stop "$unit" + + # if it is a mount unit, we can find the snap name in the mount + # unit (we just ignore unit files) + snap=$(grep "Where=${SNAP_MOUNT_DIR}/" "/etc/systemd/system/$unit"|cut -f3 -d/) + rev=$(grep "Where=${SNAP_MOUNT_DIR}/" "/etc/systemd/system/$unit"|cut -f4 -d/) + if [ -n "$snap" ]; then + echo "Removing snap $snap" + # aliases + if [ -d "${SNAP_MOUNT_DIR}/bin" ]; then + find "${SNAP_MOUNT_DIR}/bin" -maxdepth 1 -lname "$snap" -delete + find "${SNAP_MOUNT_DIR}/bin" -maxdepth 1 -lname "$snap.*" -delete + fi + # generated binaries + rm -f "${SNAP_MOUNT_DIR}/bin/$snap" + rm -f "${SNAP_MOUNT_DIR}/bin/$snap".* + # snap mount dir + umount -l "${SNAP_MOUNT_DIR}/$snap/$rev" 2> /dev/null || true + rm -rf "${SNAP_MOUNT_DIR:?}/$snap/$rev" + rm -f "${SNAP_MOUNT_DIR}/$snap/current" + # snap data dir + rm -rf "/var/snap/$snap/$rev" + rm -rf "/var/snap/$snap/common" + rm -f "/var/snap/$snap/current" + # opportunistic remove (may fail if there are still revisions left) + for d in "${SNAP_MOUNT_DIR}/$snap" "/var/snap/$snap"; do + if [ -d "$d" ]; then + rmdir --ignore-fail-on-non-empty "$d" + fi + done + fi + + echo "Removing $unit" + rm -f "/etc/systemd/system/$unit" + rm -f "/etc/systemd/system/multi-user.target.wants/$unit" + done + + echo "Discarding preserved snap namespaces" + # opportunistic as those might not be actually mounted + for mnt in /run/snapd/ns/*.mnt; do + umount -l "$mnt" || true + rm -f "$mnt" + done + for fstab in /run/snapd/ns/*.fstab; do + rm -f "$fstab" + done + umount -l /run/snapd/ns/ || true + + + echo "Removing downloaded snaps" + rm -rf /var/lib/snapd/snaps/* + + echo "Final directory cleanup" + rm -rf "${SNAP_MOUNT_DIR}" + rm -rf /var/snap + + echo "Removing leftover snap shared state data" + rm -rf /var/lib/snapd/desktop/applications/* + rm -rf /var/lib/snapd/seccomp/bpf/* + rm -rf /var/lib/snapd/device/* + rm -rf /var/lib/snapd/assertions/* + + echo "Removing snapd catalog cache" + rm -f /var/cache/snapd/* +} + +while [ -n "$1" ]; do + case "$1" in + --help) + show_help + exit + ;; + --snap-mount-dir=*) + SNAP_MOUNT_DIR=${1#*=} + SNAP_UNIT_PREFIX=$(systemd-escape -p "$SNAP_MOUNT_DIR") + shift + ;; + --purge) + purge + shift + ;; + *) + echo "Unknown command: $1" + exit 1 + ;; + esac +done diff -Nru snapd-2.28.5/packaging/opensuse-42.1/snapd.changes snapd-2.29.3/packaging/opensuse-42.1/snapd.changes --- snapd-2.28.5/packaging/opensuse-42.1/snapd.changes 2017-10-13 21:25:46.000000000 +0000 +++ snapd-2.29.3/packaging/opensuse-42.1/snapd.changes 2017-11-09 18:16:29.000000000 +0000 @@ -1,4 +1,24 @@ ------------------------------------------------------------------- +Thu Nov 09 19:16:29 UTC 2017 - mvo@fastmail.fm + +- Update to upstream release 2.29.3 + +------------------------------------------------------------------- +Fri Nov 03 17:26:14:17 UTC 2017 - mvo@fastmail.fm + +- Update to upstream release 2.29.2 + +------------------------------------------------------------------- +Fri Nov 03 07:27:08:17 UTC 2017 - mvo@fastmail.fm + +- Update to upstream release 2.29.1 + +------------------------------------------------------------------- +Mon Oct 30 16:24:08 UTC 2017 - mvo@fastmail.fm + +- Update to upstream release 2.29 + +------------------------------------------------------------------- Fri Oct 13 19:46:37 UTC 2017 - mvo@fastmail.fm - Update to upstream release 2.28.4 diff -Nru snapd-2.28.5/packaging/opensuse-42.1/snapd.spec snapd-2.29.3/packaging/opensuse-42.1/snapd.spec --- snapd-2.28.5/packaging/opensuse-42.1/snapd.spec 2017-10-13 21:25:46.000000000 +0000 +++ snapd-2.29.3/packaging/opensuse-42.1/snapd.spec 2017-11-09 12:13:04.000000000 +0000 @@ -32,7 +32,7 @@ %define systemd_services_list snapd.refresh.timer snapd.refresh.service snapd.socket snapd.service snapd.autoimport.service snapd.system-shutdown.service Name: snapd -Version: 2.28.5 +Version: 2.29.3 Release: 0 Summary: Tools enabling systems to work with .snap files License: GPL-3.0 @@ -49,17 +49,18 @@ BuildRequires: golang-packaging BuildRequires: gpg2 BuildRequires: indent +BuildRequires: libapparmor-devel BuildRequires: libcap-devel BuildRequires: libseccomp-devel BuildRequires: libtool BuildRequires: libudev-devel BuildRequires: libuuid-devel BuildRequires: make +BuildRequires: openssh BuildRequires: pkg-config BuildRequires: python-docutils BuildRequires: python3-docutils BuildRequires: squashfs -BuildRequires: openssh BuildRequires: timezone BuildRequires: udev BuildRequires: xfsprogs-devel @@ -73,6 +74,7 @@ PreReq: permissions Requires(post): permissions +Requires: apparmor Requires: gpg2 Requires: openssh Requires: squashfs @@ -128,11 +130,11 @@ %goprep %{import_path} %if 0%{?with_test_keys} -# The %gobuild macro doesn't allow us to pass any additional parameters +# The gobuild macro doesn't allow us to pass any additional parameters # so we we have to invoke `go install` here manually. export GOPATH=%{_builddir}/go:%{_libdir}/go/contrib export GOBIN=%{_builddir}/go/bin -# Options used are the same as the %gobuild macro does but as it +# Options used are the same as the gobuild macro does but as it # doesn't allow us to amend new flags we have to repeat them here: # -s: tell long running tests to shorten their build time # -v: be verbose @@ -145,9 +147,11 @@ %gobuild cmd/snap %gobuild cmd/snapctl -%gobuild cmd/snap-update-ns -# build snap-exec completely static for base snaps +# build snap-exec and snap-update-ns completely static for base snaps CGO_ENABLED=0 %gobuild cmd/snap-exec +# gobuild --ldflags '-extldflags "-static"' bin/snap-update-ns +# FIXME: ^ this doesn't work yet, it's going to be fixed with another PR. +%gobuild bin/snap-update-ns # This is ok because snap-seccomp only requires static linking when it runs from the core-snap via re-exec. sed -e "s/-Bstatic -lseccomp/-Bstatic/g" -i %{_builddir}/go/src/%{provider_prefix}/cmd/snap-seccomp/main.go @@ -199,6 +203,7 @@ rm -f %{?buildroot}%{_libexecdir}/snapd/system-shutdown # Install the directories that snapd creates by itself so that they can be a part of the package install -d %buildroot/var/lib/snapd/{assertions,desktop/applications,device,hostfs,mount,apparmor/profiles,seccomp/bpf,snaps} +install -d %buildroot/var/cache/snapd install -d %buildroot/snap/bin # Install local permissions policy for snap-confine. This should be removed # once snap-confine is added to the permissions package. This is done following @@ -244,6 +249,9 @@ %preun %service_del_preun %{systemd_services_list} +if [ $1 -eq 0 ]; then + rm -f /var/cache/snapd/* +fi %postun %service_del_postun %{systemd_services_list} @@ -260,6 +268,7 @@ %dir /var/lib/snapd %dir /var/lib/snapd/apparmor %dir /var/lib/snapd/apparmor/profiles +%dir /var/lib/snapd/apparmor/snap-confine.d %dir /var/lib/snapd/assertions %dir /var/lib/snapd/desktop %dir /var/lib/snapd/desktop/applications @@ -269,8 +278,9 @@ %dir /var/lib/snapd/seccomp %dir /var/lib/snapd/seccomp/bpf %dir /var/lib/snapd/snaps +%dir /var/cache/snapd %verify(not user group mode) %attr(04755,root,root) %{_libexecdir}/snapd/snap-confine -%{_mandir}/man5/snap-confine.5.gz +%{_mandir}/man1/snap-confine.1.gz %{_mandir}/man5/snap-discard-ns.5.gz %{_udevrulesdir}/80-snappy-assign.rules %{_unitdir}/snapd.refresh.service diff -Nru snapd-2.28.5/packaging/opensuse-42.2/snapd.changes snapd-2.29.3/packaging/opensuse-42.2/snapd.changes --- snapd-2.28.5/packaging/opensuse-42.2/snapd.changes 2017-10-13 21:25:46.000000000 +0000 +++ snapd-2.29.3/packaging/opensuse-42.2/snapd.changes 2017-11-09 18:16:29.000000000 +0000 @@ -1,4 +1,24 @@ ------------------------------------------------------------------- +Thu Nov 09 19:16:29 UTC 2017 - mvo@fastmail.fm + +- Update to upstream release 2.29.3 + +------------------------------------------------------------------- +Fri Nov 03 17:26:14:17 UTC 2017 - mvo@fastmail.fm + +- Update to upstream release 2.29.2 + +------------------------------------------------------------------- +Fri Nov 03 07:27:08:17 UTC 2017 - mvo@fastmail.fm + +- Update to upstream release 2.29.1 + +------------------------------------------------------------------- +Mon Oct 30 16:24:08 UTC 2017 - mvo@fastmail.fm + +- Update to upstream release 2.29 + +------------------------------------------------------------------- Fri Oct 13 19:46:37 UTC 2017 - mvo@fastmail.fm - Update to upstream release 2.28.4 diff -Nru snapd-2.28.5/packaging/opensuse-42.2/snapd.spec snapd-2.29.3/packaging/opensuse-42.2/snapd.spec --- snapd-2.28.5/packaging/opensuse-42.2/snapd.spec 2017-10-13 21:25:46.000000000 +0000 +++ snapd-2.29.3/packaging/opensuse-42.2/snapd.spec 2017-11-09 12:13:04.000000000 +0000 @@ -32,7 +32,7 @@ %define systemd_services_list snapd.refresh.timer snapd.refresh.service snapd.socket snapd.service snapd.autoimport.service snapd.system-shutdown.service Name: snapd -Version: 2.28.5 +Version: 2.29.3 Release: 0 Summary: Tools enabling systems to work with .snap files License: GPL-3.0 @@ -49,17 +49,18 @@ BuildRequires: golang-packaging BuildRequires: gpg2 BuildRequires: indent +BuildRequires: libapparmor-devel BuildRequires: libcap-devel BuildRequires: libseccomp-devel BuildRequires: libtool BuildRequires: libudev-devel BuildRequires: libuuid-devel BuildRequires: make +BuildRequires: openssh BuildRequires: pkg-config BuildRequires: python-docutils BuildRequires: python3-docutils BuildRequires: squashfs -BuildRequires: openssh BuildRequires: timezone BuildRequires: udev BuildRequires: xfsprogs-devel @@ -73,6 +74,7 @@ PreReq: permissions Requires(post): permissions +Requires: apparmor Requires: gpg2 Requires: openssh Requires: squashfs @@ -128,11 +130,11 @@ %goprep %{import_path} %if 0%{?with_test_keys} -# The %gobuild macro doesn't allow us to pass any additional parameters +# The gobuild macro doesn't allow us to pass any additional parameters # so we we have to invoke `go install` here manually. export GOPATH=%{_builddir}/go:%{_libdir}/go/contrib export GOBIN=%{_builddir}/go/bin -# Options used are the same as the %gobuild macro does but as it +# Options used are the same as the gobuild macro does but as it # doesn't allow us to amend new flags we have to repeat them here: # -s: tell long running tests to shorten their build time # -v: be verbose @@ -145,9 +147,11 @@ %gobuild cmd/snap %gobuild cmd/snapctl -%gobuild cmd/snap-update-ns -# build snap-exec completely static for base snaps +# build snap-exec and snap-update-ns completely static for base snaps CGO_ENABLED=0 %gobuild cmd/snap-exec +# gobuild --ldflags '-extldflags "-static"' bin/snap-update-ns +# FIXME: ^ this doesn't work yet, it's going to be fixed with another PR. +%gobuild bin/snap-update-ns # This is ok because snap-seccomp only requires static linking when it runs from the core-snap via re-exec. sed -e "s/-Bstatic -lseccomp/-Bstatic/g" -i %{_builddir}/go/src/%{provider_prefix}/cmd/snap-seccomp/main.go @@ -199,6 +203,7 @@ rm -f %{?buildroot}%{_libexecdir}/snapd/system-shutdown # Install the directories that snapd creates by itself so that they can be a part of the package install -d %buildroot/var/lib/snapd/{assertions,desktop/applications,device,hostfs,mount,apparmor/profiles,seccomp/bpf,snaps} +install -d %buildroot/var/cache/snapd install -d %buildroot/snap/bin # Install local permissions policy for snap-confine. This should be removed # once snap-confine is added to the permissions package. This is done following @@ -244,6 +249,9 @@ %preun %service_del_preun %{systemd_services_list} +if [ $1 -eq 0 ]; then + rm -f /var/cache/snapd/* +fi %postun %service_del_postun %{systemd_services_list} @@ -260,6 +268,7 @@ %dir /var/lib/snapd %dir /var/lib/snapd/apparmor %dir /var/lib/snapd/apparmor/profiles +%dir /var/lib/snapd/apparmor/snap-confine.d %dir /var/lib/snapd/assertions %dir /var/lib/snapd/desktop %dir /var/lib/snapd/desktop/applications @@ -269,8 +278,9 @@ %dir /var/lib/snapd/seccomp %dir /var/lib/snapd/seccomp/bpf %dir /var/lib/snapd/snaps +%dir /var/cache/snapd %verify(not user group mode) %attr(04755,root,root) %{_libexecdir}/snapd/snap-confine -%{_mandir}/man5/snap-confine.5.gz +%{_mandir}/man1/snap-confine.1.gz %{_mandir}/man5/snap-discard-ns.5.gz %{_udevrulesdir}/80-snappy-assign.rules %{_unitdir}/snapd.refresh.service diff -Nru snapd-2.28.5/packaging/ubuntu-14.04/changelog snapd-2.29.3/packaging/ubuntu-14.04/changelog --- snapd-2.28.5/packaging/ubuntu-14.04/changelog 2017-10-13 21:25:46.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-14.04/changelog 2017-11-09 18:16:29.000000000 +0000 @@ -1,3 +1,241 @@ +snapd (2.29.3~14.04) trusty; urgency=medium + + * New upstream release, LP: #1726258 + - daemon: cherry-picked /v2/logs fixes + - cmd/snap-confine: Respect biarch nature of libdirs + - cmd/snap-confine: Ensure snap-confine is allowed to access os- + release + - interfaces: fix udev tagging for hooks + - cmd: fix re-exec bug with classic confinement for host snapd + - tests: disable xdg-open-compat test + - cmd/snap-confine: add slave PTYs and let devpts newinstance + perform mediation + - interfaces/many: misc policy updates for browser-support, cups- + control and network-status + - interfaces/raw-usb: match on SUBSYSTEM, not SUBSYSTEMS + - tests: fix security-device-cgroup* tests on devices with + framebuffer + + -- Michael Vogt Thu, 09 Nov 2017 19:16:24 +0100 + +snapd (2.29.2~14.04) trusty; urgency=medium + + * New upstream release, LP: #1726258 + - snapctl: disable stop/start/restart (2.29) + - cmd/snap-update-ns: fix collection of changes made + + -- Michael Vogt Fri, 03 Nov 2017 17:17:47 +0100 + +snapd (2.29.1~14.04) trusty; urgency=medium + + * New upstream release, LP: #1726258 + - interfaces: fix incorrect signature of ofono DBusPermanentSlot + - interfaces/serial-port: udev tag plugged slots that have just + 'path' via KERNEL + - interfaces/hidraw: udev tag plugged slots that have just 'path' + via KERNEL + - interfaces/uhid: unconditionally add existing uhid device to the + device cgroup + - cmd/snap-update-ns: fix mount rules for font sharing + - tests: disable refresh-undo test on trusty for now + - tests: use `snap change --last=install` in snapd-reexec test + - Revert " wrappers: fail install if exec-line cannot be re-written + - interfaces: don't udev tag devmode or classic snaps + - many: make ignore-validation sticky and send the flag with refresh + requests + + -- Michael Vogt Fri, 03 Nov 2017 07:25:17 +0100 + +snapd (2.29~14.04) trusty; urgency=medium + + * New upstream release, LP: #1726258 + - interfaces/many: miscellaneous updates based on feedback from the + field + - snap-confine: allow reading uevents from any where in /sys + - spread: add bionic beaver + - debian: make packaging/ubuntu-14.04/copyright a real file again + - tests: cherry pick the fix for services test into 2.29 + - cmd/snap-update-ns: initialize logger + - hooks/configure: queue service restarts + - snap-{confine,seccomp}: make @unrestricted fully unrestricted + - interfaces: clean system apparmor cache on core device + - debian: do not build static snap-exec on powerpc + - snap-confine: increase sanity_timeout to 6s + - snapctl: cherry pick service commands changes + - cmd/snap: tell translators about arg names and descs req's + - systemd: run all mount units before snapd.service to avoid race + - store: add a test to show auth failures are forwarded by doRequest + - daemon: convert ErrInvalidCredentials to a 401 Unauthorized error. + - store: forward on INVALID_CREDENTIALS error as + ErrInvalidCredentials + - daemon: generate a forbidden response message if polkit dialog is + dismissed + - daemon: Allow Polkit authorization to cancel changes. + - travis: switch to container based test runs + - interfaces: reduce duplicated code in interface tests mocks + - tests: improve revert related testing + - interfaces: sanitize plugs and slots early in ReadInfo + - store: add download caching + - preserve TMPDIR and HOSTALIASES across snap-confine invocation + - snap-confine: init all arrays with `= {0,}` + - tests: adding test for network-manager interface + - interfaces/mount: don't generate legacy per-hook/per-app mount + profiles + - snap: introduce structured epochs + - tests: fix interfaces-cups-control test for cups-2.2.5 + - snap-confine: cleanup incorrectly created nvidia udev tags + - cmd/snap-confine: update valid security tag regexp + - cmd/libsnap: enable two stranded tests + - cmd,packaging: enable apparmor on openSUSE + - overlord/ifacestate: refresh all security backends on startup + - interfaces/dbus: drop unneeded check for + release.ReleaseInfo.ForceDevMode + - dbus: ensure io.snapcraft.Launcher.service is created on re- + exec + - overlord/auth: continue for now supporting UBUNTU_STORE_ID if the + model is generic-classic + - snap-confine: add support for handling /dev/nvidia-modeset + - interfaces/network-control: remove incorrect rules for tun + - spread: allow setting SPREAD_DEBUG_EACH=0 to disable debug-each + section + - packaging: remove .mnt files on removal + - tests: fix econnreset scenario when the iptables rule was not + created + - tests: add test for lxd interface + - run-checks: use nakedret static checker to check for naked + returns on long functions + - progress: be more flexible in testing ansimeter + - interfaces: fix udev rules for tun + - many: implement our own ANSI-escape-using progress indicator + - snap-exec: update tests to follow main_test pattern + - snap: support "command: foo $ENV_STRING" + - packaging: update nvidia configure options + - snap: add new `snap pack` and use in tests + - cmd: correctly name the "Ubuntu" and "Arch" NVIDIA methods + - cmd: add autogen case for solus + - tests: do not use http://canihazip.com/ which appears to be down + - hooks: commands for controlling own services from snapctl + - snap: refactor cmdGet.Execute() + - interfaces/mount: make Change.Perform testable and test it + - interfaces/mount,cmd/snap-update-ns: move change code + - snap-confine: is_running_on_classic_distribution() looks into os- + release + - interfaces: misc updates for default, browser-support, home and + system-observe + - interfaces: deny lttng by default + - interfaces/lxd: lxd slot implementation can also be an app snap + - release,cmd,dirs: Redo the distro checks to take into account + distribution families + - cmd/snap: completion for alias and unalias + - snap-confine: add new SC_CLEANUP and use it + - snap: refrain from running filepath.Base on random strings + - cmd/snap-confine: put processes into freezer hierarchy + - wrappers: fail install if exec-line cannot be re-written + - cmd/snap-seccomp,osutil: make user/group lookup functions public + - snapstate: deal with snap user data in the /root/ directory + - interfaces: Enhance full-confinement support for biarch + distributions + - snap-confine: Only attempt to copy/mount NVIDIA libs when NVIDIA + is used + - packaging/fedora: Add Fedora 26, 27, and Rawhide symlinks + - overlord/snapstate: prefer a smaller corner case for doing the + wrong thing + - cmd/snap-repair: set user agent for snap-repair http requests + - packaging: bring down the delta between 14.04 and 16.04 + - snap-confine: Ensure lib64 biarch directory is respected + - snap-confine: update apparmor rules for fedora based base snaps + - tests: Increase SNAPD_CONFIGURE_HOOK_TIMEOUT to 3 minutes to + install real snaps + - daemon: use client.Snap instead of map[string]interface{} for + snaps. + - hooks: rename refresh hook to post-refresh + - git: make the .gitingore file a bit more targeted + - interfaces/opengl: don't udev tag nvidia devices and use snap- + confine instead + - cmd/snap-{confine,update-ns}: apply mount profiles using snap- + update-ns + - cmd: update "make hack" + - interfaces/system-observe: allow clients to enumerate DBus + connection names + - snap-repair: implement `snap-repair {list,show}` + - dirs,interfaces: create snap-confine.d on demand when re-executing + - snap-confine: fix base snaps on core + - cmd/snap-repair: fix tests when running as root + - interfaces: add Connection type + - cmd/snap-repair: skip disabled repairs + - cmd/snap-repair: prefer leaking unmanaged fds on test failure over + closing random ones + - snap-repair: make `repair` binary available for repair scripts + - snap-repair: fix missing Close() in TestStatusHappy + - cmd/snap-confine,packaging: import snapd-generated policy + - cmd/snap: return empty document if snap has no configuration + - snap-seccomp: run secondary-arch tests via gcc-multilib + - snap: implement `snap {repair,repairs}` and pass-through to snap- + repair + - interfaces/builtin: allow receiving dbus messages + - snap-repair: implement `snap-repair {done,skip,retry}` + - data/completion: small tweak to snap completion snippet + - dirs: fix classic support detection + - cmd/snap-repair: integrate root public keys for repairs + - tests: fix ubuntu core services + - tests: add new test that checks that the compat snapd-xdg-open + works + - snap-confine: improve error message if core/u-core cannot be found + - tests: only run tests/regression/nmcli on amd64 + - interfaces: mount host system fonts in desktop interface + - interfaces: enable partial apparmor support + - snapstate: auto-install missing base snaps + - spread: work around temporary packaging issue in debian sid + - asserts,cmd/snap-repair: introduce a mandatory summary for repairs + - asserts,cmd/snap-repair: represent RepairID internally as an int + - tests: test the real "xdg-open" from the core snap + - many: implement fetching sections and package names periodically. + - interfaces/network: allow using netcat as client + - snap-seccomp, osutil: use osutil.AtomicFile in snap-seccomp + - snap-seccomp: skip mknod syscall on arm64 + - tests: add trivial canonical-livepatch test + - tests: add test that ensures that all core services are working + - many: add logger.MockLogger() and use it in the tests + - snap-repair: fix test failure in TestRepairHitsTimeout + - asserts: add empty values check in HeadersFromPrimaryKey + - daemon: remove unused installSnap var in test + - daemon: reach for Overlord.Loop less thanks to overlord.Mock + - snap-seccomp: manually resolve socket() call in tests + - tests: change regex used to validate installed ubuntu core snap + - cmd/snapctl: allow snapctl -h without a context (regression fix). + - many: use snapcore/snapd/i18n instead of i18n/dumb + - many: introduce asserts.NotFoundError replacing both ErrNotFound + and store.AssertionNotFoundError + - packaging: don't include any marcos in comments + - overlord: use overlord.Mock in more tests, make sure we check the + outcome of Settle + - tests: try to fix staging tests + - store: simplify api base url config + - systemd: add systemd.MockJournalctl() + - many: provide systemd.MockSystemctl() helper + - tests: improve the listing test to not fail for e.g. 2.28~rc2 + - snapstate: give snapmgrTestSuite.settle() more time to settle + - tests: fix regex to check core version on snap list + - debian: update trusted account-keys check on 14.04 packaging + - interfaces: add udev netlink support to hardware-observe + - overlord: introduce Mock which enables to use Overlord.Settle for + settle in many more places + - snap-repair: execute the repair and capture logs/status + - tests: run the tests/unit/go everywhere + - daemon, snapstate: move ensureCore from daemon/api.go into + snapstate.go + - cmd/snap: get keys or root document + - spread.yaml: turn suse to manual given that it's breaking master + - many: configure store from state, reconfigure store at runtime + - osutil: AtomicWriter (an io.Writer), and io.Reader versions of + AtomicWrite* + - tests: check for negative syscalls in runBpf() and skip those + tests + - docs: use abolute path in PULL_REQUEST_TEMPLATE.md + - store: move device auth endpoint uris to config (#3831) + + -- Michael Vogt Mon, 30 Oct 2017 16:22:26 +0100 + snapd (2.28.5~14.04) trusty; urgency=medium * New upstream release, LP: #1714984 diff -Nru snapd-2.28.5/packaging/ubuntu-14.04/control snapd-2.29.3/packaging/ubuntu-14.04/control --- snapd-2.28.5/packaging/ubuntu-14.04/control 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-14.04/control 2017-10-23 06:17:27.000000000 +0000 @@ -13,6 +13,7 @@ dh-golang (>=1.7), dh-systemd, fakeroot, + gcc-multilib [amd64], gettext, grub-common, gnupg2, diff -Nru snapd-2.28.5/packaging/ubuntu-14.04/files snapd-2.29.3/packaging/ubuntu-14.04/files --- snapd-2.28.5/packaging/ubuntu-14.04/files 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-14.04/files 2017-11-03 06:33:32.000000000 +0000 @@ -0,0 +1 @@ +snapd_2.29.1~14.04_source.buildinfo devel optional diff -Nru snapd-2.28.5/packaging/ubuntu-14.04/gbp.conf snapd-2.29.3/packaging/ubuntu-14.04/gbp.conf --- snapd-2.28.5/packaging/ubuntu-14.04/gbp.conf 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-14.04/gbp.conf 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -[DEFAULT] -debian-branch = ubuntu/14.04 -export-dir = ../build-area -postexport = govendor sync diff -Nru snapd-2.28.5/packaging/ubuntu-14.04/rules snapd-2.29.3/packaging/ubuntu-14.04/rules --- snapd-2.28.5/packaging/ubuntu-14.04/rules 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-14.04/rules 2017-10-23 06:17:27.000000000 +0000 @@ -80,7 +80,7 @@ # for stability, predicability and easy of deployment. We need to link some # things dynamically though: udev has no stable IPC protocol between # libudev and udevd so we need to link with it dynamically. - VENDOR_ARGS=--enable-nvidia-ubuntu --enable-static-libcap --enable-static-libapparmor --enable-static-libseccomp + VENDOR_ARGS=--enable-nvidia-multiarch --enable-static-libcap --enable-static-libapparmor --enable-static-libseccomp BUILT_USING_PACKAGES=libcap-dev libapparmor-dev libseccomp-dev else ifeq ($(shell dpkg-vendor --query Vendor),Debian) @@ -131,9 +131,11 @@ # Generate static snap-exec - it somehow includes CGO so we must # force a static build here. We need a static snap-exec inside # the core snap because not all bases will have a libc - (cd _build/bin && GOPATH=$$(pwd)/.. CGO_ENABLED=0 go build $(DH_GOPKG)/cmd/snap-exec) + (cd _build/bin && GOPATH=$$(pwd)/.. CGO_ENABLED=0 go build $(GCCGOFLAGS) $(DH_GOPKG)/cmd/snap-exec) + (cd _build/bin && GOPATH=$$(pwd)/.. go build --ldflags '-extldflags "-static"' $(GCCGOFLAGS) $(DH_GOPKG)/cmd/snap-update-ns) # ensure we generated a static build $(shell if ldd _build/bin/snap-exec; then false "need static build"; fi) + $(shell if ldd _build/bin/snap-update-ns; then false "need static build"; fi) # Build C bits, sadly manually cd cmd && ( autoreconf -i -f ) @@ -151,14 +153,19 @@ [ $$(strings _build/bin/snapd|grep -c -E "public-key-sha3-384: [a-zA-Z0-9_-]{64}") -eq 2 ] strings _build/bin/snapd|grep -c "^public-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk$$" strings _build/bin/snapd|grep -c "^public-key-sha3-384: d-JcZF9nD9eBw7bwMnH61x-bklnQOhQud1Is6o_cn2wTj8EYDi9musrIT9z2MdAa$$" + # same for snap-repair + [ $$(strings _build/bin/snap-repair|grep -c -E "public-key-sha3-384: [a-zA-Z0-9_-]{64}") -eq 3 ] + # common with snapd + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk$$" + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: d-JcZF9nD9eBw7bwMnH61x-bklnQOhQud1Is6o_cn2wTj8EYDi9musrIT9z2MdAa$$" + # repair-root + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: nttW6NfBXI_E-00u38W-KH6eiksfQNXuI7IiumoV49_zkbhM0sYTzSnFlwZC-W4t$$" endif ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) # run the snap-confine tests $(MAKE) -C cmd check endif -# no dh_systemd in trusty - override_dh_install: # we do not need this in the package, its just needed during build rm -rf ${CURDIR}/debian/tmp/usr/bin/xgettext-go @@ -181,6 +188,7 @@ # it's a conf file so we're stuck with it mv debian/snapd/etc/profile.d/snapd.sh debian/snapd/etc/profile.d/apps-bin-path.sh + # trusty needs this to make /snap rshared install --mode=0644 debian/snap.mount.service debian/snapd/$(SYSTEMD_UNITS_DESTDIR) $(MAKE) -C cmd install DESTDIR=$(CURDIR)/debian/tmp diff -Nru snapd-2.28.5/packaging/ubuntu-14.04/snap-confine.maintscript snapd-2.29.3/packaging/ubuntu-14.04/snap-confine.maintscript --- snapd-2.28.5/packaging/ubuntu-14.04/snap-confine.maintscript 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-14.04/snap-confine.maintscript 2017-08-08 06:31:43.000000000 +0000 @@ -0,0 +1 @@ +rm_conffile /etc/apparmor.d/usr.lib.snapd.snap-confine 2.23.6~ diff -Nru snapd-2.28.5/packaging/ubuntu-14.04/snapd.dirs snapd-2.29.3/packaging/ubuntu-14.04/snapd.dirs --- snapd-2.28.5/packaging/ubuntu-14.04/snapd.dirs 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-14.04/snapd.dirs 2017-10-23 06:17:27.000000000 +0000 @@ -1,5 +1,7 @@ snap +var/cache/snapd usr/lib/snapd +var/lib/snapd/apparmor/snap-confine.d var/lib/snapd/auto-import var/lib/snapd/desktop var/lib/snapd/environment diff -Nru snapd-2.28.5/packaging/ubuntu-14.04/snapd.install snapd-2.29.3/packaging/ubuntu-14.04/snapd.install --- snapd-2.28.5/packaging/ubuntu-14.04/snapd.install 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-14.04/snapd.install 2017-10-23 06:17:27.000000000 +0000 @@ -24,7 +24,7 @@ lib/udev/snappy-app-dev usr/lib/snapd/snap-confine usr/lib/snapd/snap-discard-ns -usr/share/man/man5/snap-confine.5 +usr/share/man/man1/snap-confine.1 usr/share/man/man5/snap-discard-ns.5 # for compatibility with ancient snap installs that wrote the shell based # wrapper scripts instead of the modern symlinks diff -Nru snapd-2.28.5/packaging/ubuntu-14.04/snapd.maintscript snapd-2.29.3/packaging/ubuntu-14.04/snapd.maintscript --- snapd-2.28.5/packaging/ubuntu-14.04/snapd.maintscript 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-14.04/snapd.maintscript 2017-08-08 06:31:43.000000000 +0000 @@ -2,3 +2,4 @@ # we used to ship a custom grub config that is no longer needed rm_conffile /etc/grub.d/09_snappy 1.7.3ubuntu1 rm_conffile /etc/ld.so.conf.d/snappy.conf 2.0.7~ +rm_conffile /etc/apparmor.d/usr.lib.snapd.snap-confine 2.23.6~ diff -Nru snapd-2.28.5/packaging/ubuntu-14.04/snapd.postrm snapd-2.29.3/packaging/ubuntu-14.04/snapd.postrm --- snapd-2.28.5/packaging/ubuntu-14.04/snapd.postrm 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-14.04/snapd.postrm 2017-10-23 06:17:27.000000000 +0000 @@ -4,27 +4,24 @@ systemctl_stop() { unit="$1" - if systemctl is-active -q "$unit"; then - echo "Stoping $unit" + for i in $(seq 10); do + if ! systemctl is-active -q "$unit"; then + echo "$unit is stopped." + break + fi + echo "Stoping $unit [attempt $i]" systemctl stop -q "$unit" || true - fi + sleep .1 + done } if [ "$1" = "purge" ]; then - mounts=$(systemctl list-unit-files --full | grep '^snap[-.].*\.mount' | cut -f1 -d ' ') - services=$(systemctl list-unit-files --full | grep '^snap[-.].*\.service' | cut -f1 -d ' ') - - for unit in $mounts; do - # ensure its really a snap mount unit or systemd unit - if ! grep -q 'What=/var/lib/snapd/snaps/' "/etc/systemd/system/$unit" && ! grep -q 'X-Snappy=yes' "/etc/systemd/system/$unit"; then - echo "Skipping non-snapd systemd unit $unit" - continue - fi - echo "Stopping $unit" - systemctl_stop "$unit" - done + # snap.mount.service is a trusty thing + systemctl_stop snap.mount.service - systemctl stop snap.mount.service || true + units=$(systemctl list-unit-files --full | grep '^snap[-.]' | cut -f1 -d ' ' | grep -vF snap.mount.service || true) + mounts=$(echo "$units" | grep '^snap[-.].*\.mount$' || true) + services=$(echo "$units" | grep '^snap[-.].*\.service$' || true) for unit in $services $mounts; do # ensure its really a snap mount unit or systemd unit @@ -33,6 +30,9 @@ continue fi + echo "Stopping $unit" + systemctl_stop "$unit" + # if it is a mount unit, we can find the snap name in the mount # unit (we just ignore unit files) snap=$(grep 'Where=/snap/' "/etc/systemd/system/$unit"|cut -f3 -d/) @@ -80,12 +80,18 @@ # opportunistic as those might not be actually mounted for mnt in /run/snapd/ns/*.mnt; do umount -l "$mnt" || true + rm -f "$mnt" + done + for fstab in /run/snapd/ns/*.fstab; do + rm -f "$fstab" done umount -l /run/snapd/ns/ || true - # prepare removal of /snap - umount -l /snap || true - rmdir --ignore-fail-on-non-empty /snap || true + echo "Removing extra snap-confine apparmor rules" + rm -f /etc/apparmor.d/snap.core.*.usr.lib.snapd.snap-confine + + echo "Removing snapd cache" + rm -f /var/cache/snapd/* echo "Removing snapd state" rm -rf /var/lib/snapd diff -Nru snapd-2.28.5/packaging/ubuntu-14.04/snap.mount.service snapd-2.29.3/packaging/ubuntu-14.04/snap.mount.service --- snapd-2.28.5/packaging/ubuntu-14.04/snap.mount.service 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-14.04/snap.mount.service 2017-10-23 06:17:27.000000000 +0000 @@ -1,3 +1,4 @@ +# trusty needs this to make /snap rshared [Unit] Description=Apply special mount sharing to /snap directory ConditionPathExists=!/run/systemd/initctl/fifo diff -Nru snapd-2.28.5/packaging/ubuntu-16.04/changelog snapd-2.29.3/packaging/ubuntu-16.04/changelog --- snapd-2.28.5/packaging/ubuntu-16.04/changelog 2017-10-13 21:25:46.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-16.04/changelog 2017-11-09 18:16:29.000000000 +0000 @@ -1,3 +1,241 @@ +snapd (2.29.3) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - daemon: cherry-picked /v2/logs fixes + - cmd/snap-confine: Respect biarch nature of libdirs + - cmd/snap-confine: Ensure snap-confine is allowed to access os- + release + - interfaces: fix udev tagging for hooks + - cmd: fix re-exec bug with classic confinement for host snapd + - tests: disable xdg-open-compat test + - cmd/snap-confine: add slave PTYs and let devpts newinstance + perform mediation + - interfaces/many: misc policy updates for browser-support, cups- + control and network-status + - interfaces/raw-usb: match on SUBSYSTEM, not SUBSYSTEMS + - tests: fix security-device-cgroup* tests on devices with + framebuffer + + -- Michael Vogt Thu, 09 Nov 2017 19:16:29 +0100 + +snapd (2.29.2) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - snapctl: disable stop/start/restart (2.29) + - cmd/snap-update-ns: fix collection of changes made + + -- Michael Vogt Fri, 03 Nov 2017 17:17:14 +0100 + +snapd (2.29.1) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - interfaces: fix incorrect signature of ofono DBusPermanentSlot + - interfaces/serial-port: udev tag plugged slots that have just + 'path' via KERNEL + - interfaces/hidraw: udev tag plugged slots that have just 'path' + via KERNEL + - interfaces/uhid: unconditionally add existing uhid device to the + device cgroup + - cmd/snap-update-ns: fix mount rules for font sharing + - tests: disable refresh-undo test on trusty for now + - tests: use `snap change --last=install` in snapd-reexec test + - Revert " wrappers: fail install if exec-line cannot be re-written + - interfaces: don't udev tag devmode or classic snaps + - many: make ignore-validation sticky and send the flag with refresh + requests + + -- Michael Vogt Fri, 03 Nov 2017 07:25:17 +0100 + +snapd (2.29) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - interfaces/many: miscellaneous updates based on feedback from the + field + - snap-confine: allow reading uevents from any where in /sys + - spread: add bionic beaver + - debian: make packaging/ubuntu-14.04/copyright a real file again + - tests: cherry pick the fix for services test into 2.29 + - cmd/snap-update-ns: initialize logger + - hooks/configure: queue service restarts + - snap-{confine,seccomp}: make @unrestricted fully unrestricted + - interfaces: clean system apparmor cache on core device + - debian: do not build static snap-exec on powerpc + - snap-confine: increase sanity_timeout to 6s + - snapctl: cherry pick service commands changes + - cmd/snap: tell translators about arg names and descs req's + - systemd: run all mount units before snapd.service to avoid race + - store: add a test to show auth failures are forwarded by doRequest + - daemon: convert ErrInvalidCredentials to a 401 Unauthorized error. + - store: forward on INVALID_CREDENTIALS error as + ErrInvalidCredentials + - daemon: generate a forbidden response message if polkit dialog is + dismissed + - daemon: Allow Polkit authorization to cancel changes. + - travis: switch to container based test runs + - interfaces: reduce duplicated code in interface tests mocks + - tests: improve revert related testing + - interfaces: sanitize plugs and slots early in ReadInfo + - store: add download caching + - preserve TMPDIR and HOSTALIASES across snap-confine invocation + - snap-confine: init all arrays with `= {0,}` + - tests: adding test for network-manager interface + - interfaces/mount: don't generate legacy per-hook/per-app mount + profiles + - snap: introduce structured epochs + - tests: fix interfaces-cups-control test for cups-2.2.5 + - snap-confine: cleanup incorrectly created nvidia udev tags + - cmd/snap-confine: update valid security tag regexp + - cmd/libsnap: enable two stranded tests + - cmd,packaging: enable apparmor on openSUSE + - overlord/ifacestate: refresh all security backends on startup + - interfaces/dbus: drop unneeded check for + release.ReleaseInfo.ForceDevMode + - dbus: ensure io.snapcraft.Launcher.service is created on re- + exec + - overlord/auth: continue for now supporting UBUNTU_STORE_ID if the + model is generic-classic + - snap-confine: add support for handling /dev/nvidia-modeset + - interfaces/network-control: remove incorrect rules for tun + - spread: allow setting SPREAD_DEBUG_EACH=0 to disable debug-each + section + - packaging: remove .mnt files on removal + - tests: fix econnreset scenario when the iptables rule was not + created + - tests: add test for lxd interface + - run-checks: use nakedret static checker to check for naked + returns on long functions + - progress: be more flexible in testing ansimeter + - interfaces: fix udev rules for tun + - many: implement our own ANSI-escape-using progress indicator + - snap-exec: update tests to follow main_test pattern + - snap: support "command: foo $ENV_STRING" + - packaging: update nvidia configure options + - snap: add new `snap pack` and use in tests + - cmd: correctly name the "Ubuntu" and "Arch" NVIDIA methods + - cmd: add autogen case for solus + - tests: do not use http://canihazip.com/ which appears to be down + - hooks: commands for controlling own services from snapctl + - snap: refactor cmdGet.Execute() + - interfaces/mount: make Change.Perform testable and test it + - interfaces/mount,cmd/snap-update-ns: move change code + - snap-confine: is_running_on_classic_distribution() looks into os- + release + - interfaces: misc updates for default, browser-support, home and + system-observe + - interfaces: deny lttng by default + - interfaces/lxd: lxd slot implementation can also be an app snap + - release,cmd,dirs: Redo the distro checks to take into account + distribution families + - cmd/snap: completion for alias and unalias + - snap-confine: add new SC_CLEANUP and use it + - snap: refrain from running filepath.Base on random strings + - cmd/snap-confine: put processes into freezer hierarchy + - wrappers: fail install if exec-line cannot be re-written + - cmd/snap-seccomp,osutil: make user/group lookup functions public + - snapstate: deal with snap user data in the /root/ directory + - interfaces: Enhance full-confinement support for biarch + distributions + - snap-confine: Only attempt to copy/mount NVIDIA libs when NVIDIA + is used + - packaging/fedora: Add Fedora 26, 27, and Rawhide symlinks + - overlord/snapstate: prefer a smaller corner case for doing the + wrong thing + - cmd/snap-repair: set user agent for snap-repair http requests + - packaging: bring down the delta between 14.04 and 16.04 + - snap-confine: Ensure lib64 biarch directory is respected + - snap-confine: update apparmor rules for fedora based base snaps + - tests: Increase SNAPD_CONFIGURE_HOOK_TIMEOUT to 3 minutes to + install real snaps + - daemon: use client.Snap instead of map[string]interface{} for + snaps. + - hooks: rename refresh hook to post-refresh + - git: make the .gitingore file a bit more targeted + - interfaces/opengl: don't udev tag nvidia devices and use snap- + confine instead + - cmd/snap-{confine,update-ns}: apply mount profiles using snap- + update-ns + - cmd: update "make hack" + - interfaces/system-observe: allow clients to enumerate DBus + connection names + - snap-repair: implement `snap-repair {list,show}` + - dirs,interfaces: create snap-confine.d on demand when re-executing + - snap-confine: fix base snaps on core + - cmd/snap-repair: fix tests when running as root + - interfaces: add Connection type + - cmd/snap-repair: skip disabled repairs + - cmd/snap-repair: prefer leaking unmanaged fds on test failure over + closing random ones + - snap-repair: make `repair` binary available for repair scripts + - snap-repair: fix missing Close() in TestStatusHappy + - cmd/snap-confine,packaging: import snapd-generated policy + - cmd/snap: return empty document if snap has no configuration + - snap-seccomp: run secondary-arch tests via gcc-multilib + - snap: implement `snap {repair,repairs}` and pass-through to snap- + repair + - interfaces/builtin: allow receiving dbus messages + - snap-repair: implement `snap-repair {done,skip,retry}` + - data/completion: small tweak to snap completion snippet + - dirs: fix classic support detection + - cmd/snap-repair: integrate root public keys for repairs + - tests: fix ubuntu core services + - tests: add new test that checks that the compat snapd-xdg-open + works + - snap-confine: improve error message if core/u-core cannot be found + - tests: only run tests/regression/nmcli on amd64 + - interfaces: mount host system fonts in desktop interface + - interfaces: enable partial apparmor support + - snapstate: auto-install missing base snaps + - spread: work around temporary packaging issue in debian sid + - asserts,cmd/snap-repair: introduce a mandatory summary for repairs + - asserts,cmd/snap-repair: represent RepairID internally as an int + - tests: test the real "xdg-open" from the core snap + - many: implement fetching sections and package names periodically. + - interfaces/network: allow using netcat as client + - snap-seccomp, osutil: use osutil.AtomicFile in snap-seccomp + - snap-seccomp: skip mknod syscall on arm64 + - tests: add trivial canonical-livepatch test + - tests: add test that ensures that all core services are working + - many: add logger.MockLogger() and use it in the tests + - snap-repair: fix test failure in TestRepairHitsTimeout + - asserts: add empty values check in HeadersFromPrimaryKey + - daemon: remove unused installSnap var in test + - daemon: reach for Overlord.Loop less thanks to overlord.Mock + - snap-seccomp: manually resolve socket() call in tests + - tests: change regex used to validate installed ubuntu core snap + - cmd/snapctl: allow snapctl -h without a context (regression fix). + - many: use snapcore/snapd/i18n instead of i18n/dumb + - many: introduce asserts.NotFoundError replacing both ErrNotFound + and store.AssertionNotFoundError + - packaging: don't include any marcos in comments + - overlord: use overlord.Mock in more tests, make sure we check the + outcome of Settle + - tests: try to fix staging tests + - store: simplify api base url config + - systemd: add systemd.MockJournalctl() + - many: provide systemd.MockSystemctl() helper + - tests: improve the listing test to not fail for e.g. 2.28~rc2 + - snapstate: give snapmgrTestSuite.settle() more time to settle + - tests: fix regex to check core version on snap list + - debian: update trusted account-keys check on 14.04 packaging + - interfaces: add udev netlink support to hardware-observe + - overlord: introduce Mock which enables to use Overlord.Settle for + settle in many more places + - snap-repair: execute the repair and capture logs/status + - tests: run the tests/unit/go everywhere + - daemon, snapstate: move ensureCore from daemon/api.go into + snapstate.go + - cmd/snap: get keys or root document + - spread.yaml: turn suse to manual given that it's breaking master + - many: configure store from state, reconfigure store at runtime + - osutil: AtomicWriter (an io.Writer), and io.Reader versions of + AtomicWrite* + - tests: check for negative syscalls in runBpf() and skip those + tests + - docs: use abolute path in PULL_REQUEST_TEMPLATE.md + - store: move device auth endpoint uris to config (#3831) + + -- Michael Vogt Mon, 30 Oct 2017 16:22:31 +0100 + snapd (2.28.5) xenial; urgency=medium * New upstream release, LP: #1714984 diff -Nru snapd-2.28.5/packaging/ubuntu-16.04/control snapd-2.29.3/packaging/ubuntu-16.04/control --- snapd-2.28.5/packaging/ubuntu-16.04/control 2017-10-11 17:40:25.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-16.04/control 2017-10-23 06:17:27.000000000 +0000 @@ -13,6 +13,7 @@ dh-golang (>=1.7), dh-systemd, fakeroot, + gcc-multilib [amd64], gettext, grub-common, gnupg2, diff -Nru snapd-2.28.5/packaging/ubuntu-16.04/rules snapd-2.29.3/packaging/ubuntu-16.04/rules --- snapd-2.28.5/packaging/ubuntu-16.04/rules 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-16.04/rules 2017-10-27 12:24:38.000000000 +0000 @@ -70,7 +70,7 @@ # for stability, predicability and easy of deployment. We need to link some # things dynamically though: udev has no stable IPC protocol between # libudev and udevd so we need to link with it dynamically. - VENDOR_ARGS=--enable-nvidia-ubuntu --enable-static-libcap --enable-static-libapparmor --enable-static-libseccomp + VENDOR_ARGS=--enable-nvidia-multiarch --enable-static-libcap --enable-static-libapparmor --enable-static-libseccomp BUILT_USING_PACKAGES=libcap-dev libapparmor-dev libseccomp-dev else ifeq ($(shell dpkg-vendor --query Vendor),Debian) @@ -124,12 +124,19 @@ mkdir -p _build/src/$(DH_GOPKG)/cmd/snap/test-data cp -a cmd/snap/test-data/*.gpg _build/src/$(DH_GOPKG)/cmd/snap/test-data/ dh_auto_build -- $(BUILDFLAGS) $(TAGS) $(GCCGOFLAGS) - # Generate static snap-exec - it somehow includes CGO so we must - # force a static build here. We need a static snap-exec inside - # the core snap because not all bases will have a libc + + # (static linking on powerpc with cgo is broken) +ifneq ($(shell dpkg-architecture -qDEB_HOST_ARCH),powerpc) + # Generate static snap-exec and snap-update-ns - it somehow includes CGO so + # we must force a static build here. We need a static snap-{exec,update-ns} + # inside the core snap because not all bases will have a libc (cd _build/bin && GOPATH=$$(pwd)/.. CGO_ENABLED=0 go build $(GCCGOFLAGS) $(DH_GOPKG)/cmd/snap-exec) + (cd _build/bin && GOPATH=$$(pwd)/.. go build --ldflags '-extldflags "-static"' $(GCCGOFLAGS) $(DH_GOPKG)/cmd/snap-update-ns) + # ensure we generated a static build $(shell if ldd _build/bin/snap-exec; then false "need static build"; fi) + $(shell if ldd _build/bin/snap-update-ns; then false "need static build"; fi) +endif # Build C bits, sadly manually cd cmd && ( autoreconf -i -f ) @@ -147,74 +154,19 @@ [ $$(strings _build/bin/snapd|grep -c -E "public-key-sha3-384: [a-zA-Z0-9_-]{64}") -eq 2 ] strings _build/bin/snapd|grep -c "^public-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk$$" strings _build/bin/snapd|grep -c "^public-key-sha3-384: d-JcZF9nD9eBw7bwMnH61x-bklnQOhQud1Is6o_cn2wTj8EYDi9musrIT9z2MdAa$$" + # same for snap-repair + [ $$(strings _build/bin/snap-repair|grep -c -E "public-key-sha3-384: [a-zA-Z0-9_-]{64}") -eq 3 ] + # common with snapd + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk$$" + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: d-JcZF9nD9eBw7bwMnH61x-bklnQOhQud1Is6o_cn2wTj8EYDi9musrIT9z2MdAa$$" + # repair-root + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: nttW6NfBXI_E-00u38W-KH6eiksfQNXuI7IiumoV49_zkbhM0sYTzSnFlwZC-W4t$$" endif ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) # run the snap-confine tests $(MAKE) -C cmd check endif -override_dh_systemd_enable: - # enable auto-import - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.autoimport.service - # we want the auto-update timer enabled by default - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.refresh.timer - # but the auto-update service disabled - dh_systemd_enable \ - --no-enable \ - -psnapd \ - data/systemd/snapd.refresh.service - # we want the repair timer enabled by default - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.snap-repair.timer - # but the repair service disabled - dh_systemd_enable \ - --no-enable \ - -psnapd \ - data/systemd/snapd.snap-repair.service - # enable snapd - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.socket - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.service - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.system-shutdown.service - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.core-fixup.service - -override_dh_systemd_start: - # we want to start the auto-update timer - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.refresh.timer - # but not start the service - dh_systemd_start \ - --no-start \ - -psnapd \ - data/systemd/snapd.refresh.service - # start snapd - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.socket - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.service - # start autoimport - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.autoimport.service - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.core-fixup.service - override_dh_install: # we do not need this in the package, its just needed during build rm -rf ${CURDIR}/debian/tmp/usr/bin/xgettext-go diff -Nru snapd-2.28.5/packaging/ubuntu-16.04/snapd.dirs snapd-2.29.3/packaging/ubuntu-16.04/snapd.dirs --- snapd-2.28.5/packaging/ubuntu-16.04/snapd.dirs 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-16.04/snapd.dirs 2017-10-23 06:17:27.000000000 +0000 @@ -1,5 +1,7 @@ snap +var/cache/snapd usr/lib/snapd +var/lib/snapd/apparmor/snap-confine.d var/lib/snapd/auto-import var/lib/snapd/desktop var/lib/snapd/environment diff -Nru snapd-2.28.5/packaging/ubuntu-16.04/snapd.install snapd-2.29.3/packaging/ubuntu-16.04/snapd.install --- snapd-2.28.5/packaging/ubuntu-16.04/snapd.install 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-16.04/snapd.install 2017-10-23 06:17:27.000000000 +0000 @@ -24,7 +24,7 @@ lib/udev/snappy-app-dev usr/lib/snapd/snap-confine usr/lib/snapd/snap-discard-ns -usr/share/man/man5/snap-confine.5 +usr/share/man/man1/snap-confine.1 usr/share/man/man5/snap-discard-ns.5 # for compatibility with ancient snap installs that wrote the shell based # wrapper scripts instead of the modern symlinks diff -Nru snapd-2.28.5/packaging/ubuntu-16.04/snapd.postrm snapd-2.29.3/packaging/ubuntu-16.04/snapd.postrm --- snapd-2.28.5/packaging/ubuntu-16.04/snapd.postrm 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-16.04/snapd.postrm 2017-10-23 06:17:27.000000000 +0000 @@ -4,22 +4,30 @@ systemctl_stop() { unit="$1" - if systemctl is-active -q "$unit"; then - echo "Stoping $unit" + for i in $(seq 10); do + if ! systemctl is-active -q "$unit"; then + echo "$unit is stopped." + break + fi + echo "Stoping $unit [attempt $i]" systemctl stop -q "$unit" || true - fi + sleep .1 + done } if [ "$1" = "purge" ]; then # undo any bind mount to /snap that resulted from LP:#1668659 + # (that bug can't happen in trusty -- and doing this would mess up snap.mount.service there) if grep -q "/snap /snap" /proc/self/mountinfo; then umount -l /snap || true fi - mounts=$(systemctl list-unit-files --full | grep '^snap[-.].*\.mount' | cut -f1 -d ' ') - services=$(systemctl list-unit-files --full | grep '^snap[-.].*\.service' | cut -f1 -d ' ') + units=$(systemctl list-unit-files --full | grep '^snap[-.]' | cut -f1 -d ' ' | grep -vF snap.mount.service || true) + mounts=$(echo "$units" | grep '^snap[-.].*\.mount$' || true) + services=$(echo "$units" | grep '^snap[-.].*\.service$' || true) + for unit in $services $mounts; do - # ensure its really a snapp mount unit or systemd unit + # ensure its really a snap mount unit or systemd unit if ! grep -q 'What=/var/lib/snapd/snaps/' "/etc/systemd/system/$unit" && ! grep -q 'X-Snappy=yes' "/etc/systemd/system/$unit"; then echo "Skipping non-snapd systemd unit $unit" continue @@ -43,7 +51,8 @@ rm -f "/snap/bin/$snap" rm -f "/snap/bin/$snap".* # snap mount dir - umount -l "/snap/$snap/$rev" 2> /dev/null || true + # we pass -d (clean up loopback devices) for trusty compatibility + umount -d -l "/snap/$snap/$rev" 2> /dev/null || true rm -rf "/snap/$snap/$rev" rm -f "/snap/$snap/current" # snap data dir @@ -53,7 +62,7 @@ # opportunistic remove (may fail if there are still revisions left for d in "/snap/$snap" "/var/snap/$snap"; do if [ -d "$d" ]; then - rmdir --ignore-fail-on-non-empty "$d" + rmdir --ignore-fail-on-non-empty "$d" || true fi done fi @@ -74,12 +83,19 @@ # opportunistic as those might not be actually mounted for mnt in /run/snapd/ns/*.mnt; do umount -l "$mnt" || true + rm -f "$mnt" + done + for fstab in /run/snapd/ns/*.fstab; do + rm -f "$fstab" done umount -l /run/snapd/ns/ || true echo "Removing extra snap-confine apparmor rules" rm -f /etc/apparmor.d/snap.core.*.usr.lib.snapd.snap-confine + echo "Removing snapd cache" + rm -f /var/cache/snapd/* + echo "Removing snapd state" rm -rf /var/lib/snapd fi diff -Nru snapd-2.28.5/packaging/ubuntu-16.10/changelog snapd-2.29.3/packaging/ubuntu-16.10/changelog --- snapd-2.28.5/packaging/ubuntu-16.10/changelog 2017-10-13 21:25:46.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-16.10/changelog 2017-11-09 18:16:29.000000000 +0000 @@ -1,3 +1,241 @@ +snapd (2.29.3) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - daemon: cherry-picked /v2/logs fixes + - cmd/snap-confine: Respect biarch nature of libdirs + - cmd/snap-confine: Ensure snap-confine is allowed to access os- + release + - interfaces: fix udev tagging for hooks + - cmd: fix re-exec bug with classic confinement for host snapd + - tests: disable xdg-open-compat test + - cmd/snap-confine: add slave PTYs and let devpts newinstance + perform mediation + - interfaces/many: misc policy updates for browser-support, cups- + control and network-status + - interfaces/raw-usb: match on SUBSYSTEM, not SUBSYSTEMS + - tests: fix security-device-cgroup* tests on devices with + framebuffer + + -- Michael Vogt Thu, 09 Nov 2017 19:16:29 +0100 + +snapd (2.29.2) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - snapctl: disable stop/start/restart (2.29) + - cmd/snap-update-ns: fix collection of changes made + + -- Michael Vogt Fri, 03 Nov 2017 17:17:14 +0100 + +snapd (2.29.1) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - interfaces: fix incorrect signature of ofono DBusPermanentSlot + - interfaces/serial-port: udev tag plugged slots that have just + 'path' via KERNEL + - interfaces/hidraw: udev tag plugged slots that have just 'path' + via KERNEL + - interfaces/uhid: unconditionally add existing uhid device to the + device cgroup + - cmd/snap-update-ns: fix mount rules for font sharing + - tests: disable refresh-undo test on trusty for now + - tests: use `snap change --last=install` in snapd-reexec test + - Revert " wrappers: fail install if exec-line cannot be re-written + - interfaces: don't udev tag devmode or classic snaps + - many: make ignore-validation sticky and send the flag with refresh + requests + + -- Michael Vogt Fri, 03 Nov 2017 07:25:17 +0100 + +snapd (2.29) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - interfaces/many: miscellaneous updates based on feedback from the + field + - snap-confine: allow reading uevents from any where in /sys + - spread: add bionic beaver + - debian: make packaging/ubuntu-14.04/copyright a real file again + - tests: cherry pick the fix for services test into 2.29 + - cmd/snap-update-ns: initialize logger + - hooks/configure: queue service restarts + - snap-{confine,seccomp}: make @unrestricted fully unrestricted + - interfaces: clean system apparmor cache on core device + - debian: do not build static snap-exec on powerpc + - snap-confine: increase sanity_timeout to 6s + - snapctl: cherry pick service commands changes + - cmd/snap: tell translators about arg names and descs req's + - systemd: run all mount units before snapd.service to avoid race + - store: add a test to show auth failures are forwarded by doRequest + - daemon: convert ErrInvalidCredentials to a 401 Unauthorized error. + - store: forward on INVALID_CREDENTIALS error as + ErrInvalidCredentials + - daemon: generate a forbidden response message if polkit dialog is + dismissed + - daemon: Allow Polkit authorization to cancel changes. + - travis: switch to container based test runs + - interfaces: reduce duplicated code in interface tests mocks + - tests: improve revert related testing + - interfaces: sanitize plugs and slots early in ReadInfo + - store: add download caching + - preserve TMPDIR and HOSTALIASES across snap-confine invocation + - snap-confine: init all arrays with `= {0,}` + - tests: adding test for network-manager interface + - interfaces/mount: don't generate legacy per-hook/per-app mount + profiles + - snap: introduce structured epochs + - tests: fix interfaces-cups-control test for cups-2.2.5 + - snap-confine: cleanup incorrectly created nvidia udev tags + - cmd/snap-confine: update valid security tag regexp + - cmd/libsnap: enable two stranded tests + - cmd,packaging: enable apparmor on openSUSE + - overlord/ifacestate: refresh all security backends on startup + - interfaces/dbus: drop unneeded check for + release.ReleaseInfo.ForceDevMode + - dbus: ensure io.snapcraft.Launcher.service is created on re- + exec + - overlord/auth: continue for now supporting UBUNTU_STORE_ID if the + model is generic-classic + - snap-confine: add support for handling /dev/nvidia-modeset + - interfaces/network-control: remove incorrect rules for tun + - spread: allow setting SPREAD_DEBUG_EACH=0 to disable debug-each + section + - packaging: remove .mnt files on removal + - tests: fix econnreset scenario when the iptables rule was not + created + - tests: add test for lxd interface + - run-checks: use nakedret static checker to check for naked + returns on long functions + - progress: be more flexible in testing ansimeter + - interfaces: fix udev rules for tun + - many: implement our own ANSI-escape-using progress indicator + - snap-exec: update tests to follow main_test pattern + - snap: support "command: foo $ENV_STRING" + - packaging: update nvidia configure options + - snap: add new `snap pack` and use in tests + - cmd: correctly name the "Ubuntu" and "Arch" NVIDIA methods + - cmd: add autogen case for solus + - tests: do not use http://canihazip.com/ which appears to be down + - hooks: commands for controlling own services from snapctl + - snap: refactor cmdGet.Execute() + - interfaces/mount: make Change.Perform testable and test it + - interfaces/mount,cmd/snap-update-ns: move change code + - snap-confine: is_running_on_classic_distribution() looks into os- + release + - interfaces: misc updates for default, browser-support, home and + system-observe + - interfaces: deny lttng by default + - interfaces/lxd: lxd slot implementation can also be an app snap + - release,cmd,dirs: Redo the distro checks to take into account + distribution families + - cmd/snap: completion for alias and unalias + - snap-confine: add new SC_CLEANUP and use it + - snap: refrain from running filepath.Base on random strings + - cmd/snap-confine: put processes into freezer hierarchy + - wrappers: fail install if exec-line cannot be re-written + - cmd/snap-seccomp,osutil: make user/group lookup functions public + - snapstate: deal with snap user data in the /root/ directory + - interfaces: Enhance full-confinement support for biarch + distributions + - snap-confine: Only attempt to copy/mount NVIDIA libs when NVIDIA + is used + - packaging/fedora: Add Fedora 26, 27, and Rawhide symlinks + - overlord/snapstate: prefer a smaller corner case for doing the + wrong thing + - cmd/snap-repair: set user agent for snap-repair http requests + - packaging: bring down the delta between 14.04 and 16.04 + - snap-confine: Ensure lib64 biarch directory is respected + - snap-confine: update apparmor rules for fedora based base snaps + - tests: Increase SNAPD_CONFIGURE_HOOK_TIMEOUT to 3 minutes to + install real snaps + - daemon: use client.Snap instead of map[string]interface{} for + snaps. + - hooks: rename refresh hook to post-refresh + - git: make the .gitingore file a bit more targeted + - interfaces/opengl: don't udev tag nvidia devices and use snap- + confine instead + - cmd/snap-{confine,update-ns}: apply mount profiles using snap- + update-ns + - cmd: update "make hack" + - interfaces/system-observe: allow clients to enumerate DBus + connection names + - snap-repair: implement `snap-repair {list,show}` + - dirs,interfaces: create snap-confine.d on demand when re-executing + - snap-confine: fix base snaps on core + - cmd/snap-repair: fix tests when running as root + - interfaces: add Connection type + - cmd/snap-repair: skip disabled repairs + - cmd/snap-repair: prefer leaking unmanaged fds on test failure over + closing random ones + - snap-repair: make `repair` binary available for repair scripts + - snap-repair: fix missing Close() in TestStatusHappy + - cmd/snap-confine,packaging: import snapd-generated policy + - cmd/snap: return empty document if snap has no configuration + - snap-seccomp: run secondary-arch tests via gcc-multilib + - snap: implement `snap {repair,repairs}` and pass-through to snap- + repair + - interfaces/builtin: allow receiving dbus messages + - snap-repair: implement `snap-repair {done,skip,retry}` + - data/completion: small tweak to snap completion snippet + - dirs: fix classic support detection + - cmd/snap-repair: integrate root public keys for repairs + - tests: fix ubuntu core services + - tests: add new test that checks that the compat snapd-xdg-open + works + - snap-confine: improve error message if core/u-core cannot be found + - tests: only run tests/regression/nmcli on amd64 + - interfaces: mount host system fonts in desktop interface + - interfaces: enable partial apparmor support + - snapstate: auto-install missing base snaps + - spread: work around temporary packaging issue in debian sid + - asserts,cmd/snap-repair: introduce a mandatory summary for repairs + - asserts,cmd/snap-repair: represent RepairID internally as an int + - tests: test the real "xdg-open" from the core snap + - many: implement fetching sections and package names periodically. + - interfaces/network: allow using netcat as client + - snap-seccomp, osutil: use osutil.AtomicFile in snap-seccomp + - snap-seccomp: skip mknod syscall on arm64 + - tests: add trivial canonical-livepatch test + - tests: add test that ensures that all core services are working + - many: add logger.MockLogger() and use it in the tests + - snap-repair: fix test failure in TestRepairHitsTimeout + - asserts: add empty values check in HeadersFromPrimaryKey + - daemon: remove unused installSnap var in test + - daemon: reach for Overlord.Loop less thanks to overlord.Mock + - snap-seccomp: manually resolve socket() call in tests + - tests: change regex used to validate installed ubuntu core snap + - cmd/snapctl: allow snapctl -h without a context (regression fix). + - many: use snapcore/snapd/i18n instead of i18n/dumb + - many: introduce asserts.NotFoundError replacing both ErrNotFound + and store.AssertionNotFoundError + - packaging: don't include any marcos in comments + - overlord: use overlord.Mock in more tests, make sure we check the + outcome of Settle + - tests: try to fix staging tests + - store: simplify api base url config + - systemd: add systemd.MockJournalctl() + - many: provide systemd.MockSystemctl() helper + - tests: improve the listing test to not fail for e.g. 2.28~rc2 + - snapstate: give snapmgrTestSuite.settle() more time to settle + - tests: fix regex to check core version on snap list + - debian: update trusted account-keys check on 14.04 packaging + - interfaces: add udev netlink support to hardware-observe + - overlord: introduce Mock which enables to use Overlord.Settle for + settle in many more places + - snap-repair: execute the repair and capture logs/status + - tests: run the tests/unit/go everywhere + - daemon, snapstate: move ensureCore from daemon/api.go into + snapstate.go + - cmd/snap: get keys or root document + - spread.yaml: turn suse to manual given that it's breaking master + - many: configure store from state, reconfigure store at runtime + - osutil: AtomicWriter (an io.Writer), and io.Reader versions of + AtomicWrite* + - tests: check for negative syscalls in runBpf() and skip those + tests + - docs: use abolute path in PULL_REQUEST_TEMPLATE.md + - store: move device auth endpoint uris to config (#3831) + + -- Michael Vogt Mon, 30 Oct 2017 16:22:31 +0100 + snapd (2.28.5) xenial; urgency=medium * New upstream release, LP: #1714984 diff -Nru snapd-2.28.5/packaging/ubuntu-16.10/control snapd-2.29.3/packaging/ubuntu-16.10/control --- snapd-2.28.5/packaging/ubuntu-16.10/control 2017-10-11 17:40:25.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-16.10/control 2017-10-23 06:17:27.000000000 +0000 @@ -13,6 +13,7 @@ dh-golang (>=1.7), dh-systemd, fakeroot, + gcc-multilib [amd64], gettext, grub-common, gnupg2, diff -Nru snapd-2.28.5/packaging/ubuntu-16.10/rules snapd-2.29.3/packaging/ubuntu-16.10/rules --- snapd-2.28.5/packaging/ubuntu-16.10/rules 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-16.10/rules 2017-10-27 12:24:38.000000000 +0000 @@ -70,7 +70,7 @@ # for stability, predicability and easy of deployment. We need to link some # things dynamically though: udev has no stable IPC protocol between # libudev and udevd so we need to link with it dynamically. - VENDOR_ARGS=--enable-nvidia-ubuntu --enable-static-libcap --enable-static-libapparmor --enable-static-libseccomp + VENDOR_ARGS=--enable-nvidia-multiarch --enable-static-libcap --enable-static-libapparmor --enable-static-libseccomp BUILT_USING_PACKAGES=libcap-dev libapparmor-dev libseccomp-dev else ifeq ($(shell dpkg-vendor --query Vendor),Debian) @@ -124,12 +124,19 @@ mkdir -p _build/src/$(DH_GOPKG)/cmd/snap/test-data cp -a cmd/snap/test-data/*.gpg _build/src/$(DH_GOPKG)/cmd/snap/test-data/ dh_auto_build -- $(BUILDFLAGS) $(TAGS) $(GCCGOFLAGS) - # Generate static snap-exec - it somehow includes CGO so we must - # force a static build here. We need a static snap-exec inside - # the core snap because not all bases will have a libc + + # (static linking on powerpc with cgo is broken) +ifneq ($(shell dpkg-architecture -qDEB_HOST_ARCH),powerpc) + # Generate static snap-exec and snap-update-ns - it somehow includes CGO so + # we must force a static build here. We need a static snap-{exec,update-ns} + # inside the core snap because not all bases will have a libc (cd _build/bin && GOPATH=$$(pwd)/.. CGO_ENABLED=0 go build $(GCCGOFLAGS) $(DH_GOPKG)/cmd/snap-exec) + (cd _build/bin && GOPATH=$$(pwd)/.. go build --ldflags '-extldflags "-static"' $(GCCGOFLAGS) $(DH_GOPKG)/cmd/snap-update-ns) + # ensure we generated a static build $(shell if ldd _build/bin/snap-exec; then false "need static build"; fi) + $(shell if ldd _build/bin/snap-update-ns; then false "need static build"; fi) +endif # Build C bits, sadly manually cd cmd && ( autoreconf -i -f ) @@ -147,74 +154,19 @@ [ $$(strings _build/bin/snapd|grep -c -E "public-key-sha3-384: [a-zA-Z0-9_-]{64}") -eq 2 ] strings _build/bin/snapd|grep -c "^public-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk$$" strings _build/bin/snapd|grep -c "^public-key-sha3-384: d-JcZF9nD9eBw7bwMnH61x-bklnQOhQud1Is6o_cn2wTj8EYDi9musrIT9z2MdAa$$" + # same for snap-repair + [ $$(strings _build/bin/snap-repair|grep -c -E "public-key-sha3-384: [a-zA-Z0-9_-]{64}") -eq 3 ] + # common with snapd + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk$$" + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: d-JcZF9nD9eBw7bwMnH61x-bklnQOhQud1Is6o_cn2wTj8EYDi9musrIT9z2MdAa$$" + # repair-root + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: nttW6NfBXI_E-00u38W-KH6eiksfQNXuI7IiumoV49_zkbhM0sYTzSnFlwZC-W4t$$" endif ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) # run the snap-confine tests $(MAKE) -C cmd check endif -override_dh_systemd_enable: - # enable auto-import - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.autoimport.service - # we want the auto-update timer enabled by default - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.refresh.timer - # but the auto-update service disabled - dh_systemd_enable \ - --no-enable \ - -psnapd \ - data/systemd/snapd.refresh.service - # we want the repair timer enabled by default - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.snap-repair.timer - # but the repair service disabled - dh_systemd_enable \ - --no-enable \ - -psnapd \ - data/systemd/snapd.snap-repair.service - # enable snapd - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.socket - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.service - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.system-shutdown.service - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.core-fixup.service - -override_dh_systemd_start: - # we want to start the auto-update timer - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.refresh.timer - # but not start the service - dh_systemd_start \ - --no-start \ - -psnapd \ - data/systemd/snapd.refresh.service - # start snapd - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.socket - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.service - # start autoimport - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.autoimport.service - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.core-fixup.service - override_dh_install: # we do not need this in the package, its just needed during build rm -rf ${CURDIR}/debian/tmp/usr/bin/xgettext-go diff -Nru snapd-2.28.5/packaging/ubuntu-16.10/snapd.dirs snapd-2.29.3/packaging/ubuntu-16.10/snapd.dirs --- snapd-2.28.5/packaging/ubuntu-16.10/snapd.dirs 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-16.10/snapd.dirs 2017-10-23 06:17:27.000000000 +0000 @@ -1,5 +1,7 @@ snap +var/cache/snapd usr/lib/snapd +var/lib/snapd/apparmor/snap-confine.d var/lib/snapd/auto-import var/lib/snapd/desktop var/lib/snapd/environment diff -Nru snapd-2.28.5/packaging/ubuntu-16.10/snapd.install snapd-2.29.3/packaging/ubuntu-16.10/snapd.install --- snapd-2.28.5/packaging/ubuntu-16.10/snapd.install 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-16.10/snapd.install 2017-10-23 06:17:27.000000000 +0000 @@ -24,7 +24,7 @@ lib/udev/snappy-app-dev usr/lib/snapd/snap-confine usr/lib/snapd/snap-discard-ns -usr/share/man/man5/snap-confine.5 +usr/share/man/man1/snap-confine.1 usr/share/man/man5/snap-discard-ns.5 # for compatibility with ancient snap installs that wrote the shell based # wrapper scripts instead of the modern symlinks diff -Nru snapd-2.28.5/packaging/ubuntu-16.10/snapd.postrm snapd-2.29.3/packaging/ubuntu-16.10/snapd.postrm --- snapd-2.28.5/packaging/ubuntu-16.10/snapd.postrm 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-16.10/snapd.postrm 2017-10-23 06:17:27.000000000 +0000 @@ -4,22 +4,30 @@ systemctl_stop() { unit="$1" - if systemctl is-active -q "$unit"; then - echo "Stoping $unit" + for i in $(seq 10); do + if ! systemctl is-active -q "$unit"; then + echo "$unit is stopped." + break + fi + echo "Stoping $unit [attempt $i]" systemctl stop -q "$unit" || true - fi + sleep .1 + done } if [ "$1" = "purge" ]; then # undo any bind mount to /snap that resulted from LP:#1668659 + # (that bug can't happen in trusty -- and doing this would mess up snap.mount.service there) if grep -q "/snap /snap" /proc/self/mountinfo; then umount -l /snap || true fi - mounts=$(systemctl list-unit-files --full | grep '^snap[-.].*\.mount' | cut -f1 -d ' ') - services=$(systemctl list-unit-files --full | grep '^snap[-.].*\.service' | cut -f1 -d ' ') + units=$(systemctl list-unit-files --full | grep '^snap[-.]' | cut -f1 -d ' ' | grep -vF snap.mount.service || true) + mounts=$(echo "$units" | grep '^snap[-.].*\.mount$' || true) + services=$(echo "$units" | grep '^snap[-.].*\.service$' || true) + for unit in $services $mounts; do - # ensure its really a snapp mount unit or systemd unit + # ensure its really a snap mount unit or systemd unit if ! grep -q 'What=/var/lib/snapd/snaps/' "/etc/systemd/system/$unit" && ! grep -q 'X-Snappy=yes' "/etc/systemd/system/$unit"; then echo "Skipping non-snapd systemd unit $unit" continue @@ -43,7 +51,8 @@ rm -f "/snap/bin/$snap" rm -f "/snap/bin/$snap".* # snap mount dir - umount -l "/snap/$snap/$rev" 2> /dev/null || true + # we pass -d (clean up loopback devices) for trusty compatibility + umount -d -l "/snap/$snap/$rev" 2> /dev/null || true rm -rf "/snap/$snap/$rev" rm -f "/snap/$snap/current" # snap data dir @@ -53,7 +62,7 @@ # opportunistic remove (may fail if there are still revisions left for d in "/snap/$snap" "/var/snap/$snap"; do if [ -d "$d" ]; then - rmdir --ignore-fail-on-non-empty "$d" + rmdir --ignore-fail-on-non-empty "$d" || true fi done fi @@ -74,12 +83,19 @@ # opportunistic as those might not be actually mounted for mnt in /run/snapd/ns/*.mnt; do umount -l "$mnt" || true + rm -f "$mnt" + done + for fstab in /run/snapd/ns/*.fstab; do + rm -f "$fstab" done umount -l /run/snapd/ns/ || true echo "Removing extra snap-confine apparmor rules" rm -f /etc/apparmor.d/snap.core.*.usr.lib.snapd.snap-confine + echo "Removing snapd cache" + rm -f /var/cache/snapd/* + echo "Removing snapd state" rm -rf /var/lib/snapd fi diff -Nru snapd-2.28.5/packaging/ubuntu-17.04/changelog snapd-2.29.3/packaging/ubuntu-17.04/changelog --- snapd-2.28.5/packaging/ubuntu-17.04/changelog 2017-10-13 21:25:46.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-17.04/changelog 2017-11-09 18:16:29.000000000 +0000 @@ -1,3 +1,241 @@ +snapd (2.29.3) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - daemon: cherry-picked /v2/logs fixes + - cmd/snap-confine: Respect biarch nature of libdirs + - cmd/snap-confine: Ensure snap-confine is allowed to access os- + release + - interfaces: fix udev tagging for hooks + - cmd: fix re-exec bug with classic confinement for host snapd + - tests: disable xdg-open-compat test + - cmd/snap-confine: add slave PTYs and let devpts newinstance + perform mediation + - interfaces/many: misc policy updates for browser-support, cups- + control and network-status + - interfaces/raw-usb: match on SUBSYSTEM, not SUBSYSTEMS + - tests: fix security-device-cgroup* tests on devices with + framebuffer + + -- Michael Vogt Thu, 09 Nov 2017 19:16:29 +0100 + +snapd (2.29.2) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - snapctl: disable stop/start/restart (2.29) + - cmd/snap-update-ns: fix collection of changes made + + -- Michael Vogt Fri, 03 Nov 2017 17:17:14 +0100 + +snapd (2.29.1) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - interfaces: fix incorrect signature of ofono DBusPermanentSlot + - interfaces/serial-port: udev tag plugged slots that have just + 'path' via KERNEL + - interfaces/hidraw: udev tag plugged slots that have just 'path' + via KERNEL + - interfaces/uhid: unconditionally add existing uhid device to the + device cgroup + - cmd/snap-update-ns: fix mount rules for font sharing + - tests: disable refresh-undo test on trusty for now + - tests: use `snap change --last=install` in snapd-reexec test + - Revert " wrappers: fail install if exec-line cannot be re-written + - interfaces: don't udev tag devmode or classic snaps + - many: make ignore-validation sticky and send the flag with refresh + requests + + -- Michael Vogt Fri, 03 Nov 2017 07:25:17 +0100 + +snapd (2.29) xenial; urgency=medium + + * New upstream release, LP: #1726258 + - interfaces/many: miscellaneous updates based on feedback from the + field + - snap-confine: allow reading uevents from any where in /sys + - spread: add bionic beaver + - debian: make packaging/ubuntu-14.04/copyright a real file again + - tests: cherry pick the fix for services test into 2.29 + - cmd/snap-update-ns: initialize logger + - hooks/configure: queue service restarts + - snap-{confine,seccomp}: make @unrestricted fully unrestricted + - interfaces: clean system apparmor cache on core device + - debian: do not build static snap-exec on powerpc + - snap-confine: increase sanity_timeout to 6s + - snapctl: cherry pick service commands changes + - cmd/snap: tell translators about arg names and descs req's + - systemd: run all mount units before snapd.service to avoid race + - store: add a test to show auth failures are forwarded by doRequest + - daemon: convert ErrInvalidCredentials to a 401 Unauthorized error. + - store: forward on INVALID_CREDENTIALS error as + ErrInvalidCredentials + - daemon: generate a forbidden response message if polkit dialog is + dismissed + - daemon: Allow Polkit authorization to cancel changes. + - travis: switch to container based test runs + - interfaces: reduce duplicated code in interface tests mocks + - tests: improve revert related testing + - interfaces: sanitize plugs and slots early in ReadInfo + - store: add download caching + - preserve TMPDIR and HOSTALIASES across snap-confine invocation + - snap-confine: init all arrays with `= {0,}` + - tests: adding test for network-manager interface + - interfaces/mount: don't generate legacy per-hook/per-app mount + profiles + - snap: introduce structured epochs + - tests: fix interfaces-cups-control test for cups-2.2.5 + - snap-confine: cleanup incorrectly created nvidia udev tags + - cmd/snap-confine: update valid security tag regexp + - cmd/libsnap: enable two stranded tests + - cmd,packaging: enable apparmor on openSUSE + - overlord/ifacestate: refresh all security backends on startup + - interfaces/dbus: drop unneeded check for + release.ReleaseInfo.ForceDevMode + - dbus: ensure io.snapcraft.Launcher.service is created on re- + exec + - overlord/auth: continue for now supporting UBUNTU_STORE_ID if the + model is generic-classic + - snap-confine: add support for handling /dev/nvidia-modeset + - interfaces/network-control: remove incorrect rules for tun + - spread: allow setting SPREAD_DEBUG_EACH=0 to disable debug-each + section + - packaging: remove .mnt files on removal + - tests: fix econnreset scenario when the iptables rule was not + created + - tests: add test for lxd interface + - run-checks: use nakedret static checker to check for naked + returns on long functions + - progress: be more flexible in testing ansimeter + - interfaces: fix udev rules for tun + - many: implement our own ANSI-escape-using progress indicator + - snap-exec: update tests to follow main_test pattern + - snap: support "command: foo $ENV_STRING" + - packaging: update nvidia configure options + - snap: add new `snap pack` and use in tests + - cmd: correctly name the "Ubuntu" and "Arch" NVIDIA methods + - cmd: add autogen case for solus + - tests: do not use http://canihazip.com/ which appears to be down + - hooks: commands for controlling own services from snapctl + - snap: refactor cmdGet.Execute() + - interfaces/mount: make Change.Perform testable and test it + - interfaces/mount,cmd/snap-update-ns: move change code + - snap-confine: is_running_on_classic_distribution() looks into os- + release + - interfaces: misc updates for default, browser-support, home and + system-observe + - interfaces: deny lttng by default + - interfaces/lxd: lxd slot implementation can also be an app snap + - release,cmd,dirs: Redo the distro checks to take into account + distribution families + - cmd/snap: completion for alias and unalias + - snap-confine: add new SC_CLEANUP and use it + - snap: refrain from running filepath.Base on random strings + - cmd/snap-confine: put processes into freezer hierarchy + - wrappers: fail install if exec-line cannot be re-written + - cmd/snap-seccomp,osutil: make user/group lookup functions public + - snapstate: deal with snap user data in the /root/ directory + - interfaces: Enhance full-confinement support for biarch + distributions + - snap-confine: Only attempt to copy/mount NVIDIA libs when NVIDIA + is used + - packaging/fedora: Add Fedora 26, 27, and Rawhide symlinks + - overlord/snapstate: prefer a smaller corner case for doing the + wrong thing + - cmd/snap-repair: set user agent for snap-repair http requests + - packaging: bring down the delta between 14.04 and 16.04 + - snap-confine: Ensure lib64 biarch directory is respected + - snap-confine: update apparmor rules for fedora based base snaps + - tests: Increase SNAPD_CONFIGURE_HOOK_TIMEOUT to 3 minutes to + install real snaps + - daemon: use client.Snap instead of map[string]interface{} for + snaps. + - hooks: rename refresh hook to post-refresh + - git: make the .gitingore file a bit more targeted + - interfaces/opengl: don't udev tag nvidia devices and use snap- + confine instead + - cmd/snap-{confine,update-ns}: apply mount profiles using snap- + update-ns + - cmd: update "make hack" + - interfaces/system-observe: allow clients to enumerate DBus + connection names + - snap-repair: implement `snap-repair {list,show}` + - dirs,interfaces: create snap-confine.d on demand when re-executing + - snap-confine: fix base snaps on core + - cmd/snap-repair: fix tests when running as root + - interfaces: add Connection type + - cmd/snap-repair: skip disabled repairs + - cmd/snap-repair: prefer leaking unmanaged fds on test failure over + closing random ones + - snap-repair: make `repair` binary available for repair scripts + - snap-repair: fix missing Close() in TestStatusHappy + - cmd/snap-confine,packaging: import snapd-generated policy + - cmd/snap: return empty document if snap has no configuration + - snap-seccomp: run secondary-arch tests via gcc-multilib + - snap: implement `snap {repair,repairs}` and pass-through to snap- + repair + - interfaces/builtin: allow receiving dbus messages + - snap-repair: implement `snap-repair {done,skip,retry}` + - data/completion: small tweak to snap completion snippet + - dirs: fix classic support detection + - cmd/snap-repair: integrate root public keys for repairs + - tests: fix ubuntu core services + - tests: add new test that checks that the compat snapd-xdg-open + works + - snap-confine: improve error message if core/u-core cannot be found + - tests: only run tests/regression/nmcli on amd64 + - interfaces: mount host system fonts in desktop interface + - interfaces: enable partial apparmor support + - snapstate: auto-install missing base snaps + - spread: work around temporary packaging issue in debian sid + - asserts,cmd/snap-repair: introduce a mandatory summary for repairs + - asserts,cmd/snap-repair: represent RepairID internally as an int + - tests: test the real "xdg-open" from the core snap + - many: implement fetching sections and package names periodically. + - interfaces/network: allow using netcat as client + - snap-seccomp, osutil: use osutil.AtomicFile in snap-seccomp + - snap-seccomp: skip mknod syscall on arm64 + - tests: add trivial canonical-livepatch test + - tests: add test that ensures that all core services are working + - many: add logger.MockLogger() and use it in the tests + - snap-repair: fix test failure in TestRepairHitsTimeout + - asserts: add empty values check in HeadersFromPrimaryKey + - daemon: remove unused installSnap var in test + - daemon: reach for Overlord.Loop less thanks to overlord.Mock + - snap-seccomp: manually resolve socket() call in tests + - tests: change regex used to validate installed ubuntu core snap + - cmd/snapctl: allow snapctl -h without a context (regression fix). + - many: use snapcore/snapd/i18n instead of i18n/dumb + - many: introduce asserts.NotFoundError replacing both ErrNotFound + and store.AssertionNotFoundError + - packaging: don't include any marcos in comments + - overlord: use overlord.Mock in more tests, make sure we check the + outcome of Settle + - tests: try to fix staging tests + - store: simplify api base url config + - systemd: add systemd.MockJournalctl() + - many: provide systemd.MockSystemctl() helper + - tests: improve the listing test to not fail for e.g. 2.28~rc2 + - snapstate: give snapmgrTestSuite.settle() more time to settle + - tests: fix regex to check core version on snap list + - debian: update trusted account-keys check on 14.04 packaging + - interfaces: add udev netlink support to hardware-observe + - overlord: introduce Mock which enables to use Overlord.Settle for + settle in many more places + - snap-repair: execute the repair and capture logs/status + - tests: run the tests/unit/go everywhere + - daemon, snapstate: move ensureCore from daemon/api.go into + snapstate.go + - cmd/snap: get keys or root document + - spread.yaml: turn suse to manual given that it's breaking master + - many: configure store from state, reconfigure store at runtime + - osutil: AtomicWriter (an io.Writer), and io.Reader versions of + AtomicWrite* + - tests: check for negative syscalls in runBpf() and skip those + tests + - docs: use abolute path in PULL_REQUEST_TEMPLATE.md + - store: move device auth endpoint uris to config (#3831) + + -- Michael Vogt Mon, 30 Oct 2017 16:22:31 +0100 + snapd (2.28.5) xenial; urgency=medium * New upstream release, LP: #1714984 diff -Nru snapd-2.28.5/packaging/ubuntu-17.04/control snapd-2.29.3/packaging/ubuntu-17.04/control --- snapd-2.28.5/packaging/ubuntu-17.04/control 2017-10-11 17:40:25.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-17.04/control 2017-10-23 06:17:27.000000000 +0000 @@ -13,6 +13,7 @@ dh-golang (>=1.7), dh-systemd, fakeroot, + gcc-multilib [amd64], gettext, grub-common, gnupg2, diff -Nru snapd-2.28.5/packaging/ubuntu-17.04/rules snapd-2.29.3/packaging/ubuntu-17.04/rules --- snapd-2.28.5/packaging/ubuntu-17.04/rules 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-17.04/rules 2017-10-27 12:24:38.000000000 +0000 @@ -70,7 +70,7 @@ # for stability, predicability and easy of deployment. We need to link some # things dynamically though: udev has no stable IPC protocol between # libudev and udevd so we need to link with it dynamically. - VENDOR_ARGS=--enable-nvidia-ubuntu --enable-static-libcap --enable-static-libapparmor --enable-static-libseccomp + VENDOR_ARGS=--enable-nvidia-multiarch --enable-static-libcap --enable-static-libapparmor --enable-static-libseccomp BUILT_USING_PACKAGES=libcap-dev libapparmor-dev libseccomp-dev else ifeq ($(shell dpkg-vendor --query Vendor),Debian) @@ -124,12 +124,19 @@ mkdir -p _build/src/$(DH_GOPKG)/cmd/snap/test-data cp -a cmd/snap/test-data/*.gpg _build/src/$(DH_GOPKG)/cmd/snap/test-data/ dh_auto_build -- $(BUILDFLAGS) $(TAGS) $(GCCGOFLAGS) - # Generate static snap-exec - it somehow includes CGO so we must - # force a static build here. We need a static snap-exec inside - # the core snap because not all bases will have a libc + + # (static linking on powerpc with cgo is broken) +ifneq ($(shell dpkg-architecture -qDEB_HOST_ARCH),powerpc) + # Generate static snap-exec and snap-update-ns - it somehow includes CGO so + # we must force a static build here. We need a static snap-{exec,update-ns} + # inside the core snap because not all bases will have a libc (cd _build/bin && GOPATH=$$(pwd)/.. CGO_ENABLED=0 go build $(GCCGOFLAGS) $(DH_GOPKG)/cmd/snap-exec) + (cd _build/bin && GOPATH=$$(pwd)/.. go build --ldflags '-extldflags "-static"' $(GCCGOFLAGS) $(DH_GOPKG)/cmd/snap-update-ns) + # ensure we generated a static build $(shell if ldd _build/bin/snap-exec; then false "need static build"; fi) + $(shell if ldd _build/bin/snap-update-ns; then false "need static build"; fi) +endif # Build C bits, sadly manually cd cmd && ( autoreconf -i -f ) @@ -147,74 +154,19 @@ [ $$(strings _build/bin/snapd|grep -c -E "public-key-sha3-384: [a-zA-Z0-9_-]{64}") -eq 2 ] strings _build/bin/snapd|grep -c "^public-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk$$" strings _build/bin/snapd|grep -c "^public-key-sha3-384: d-JcZF9nD9eBw7bwMnH61x-bklnQOhQud1Is6o_cn2wTj8EYDi9musrIT9z2MdAa$$" + # same for snap-repair + [ $$(strings _build/bin/snap-repair|grep -c -E "public-key-sha3-384: [a-zA-Z0-9_-]{64}") -eq 3 ] + # common with snapd + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk$$" + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: d-JcZF9nD9eBw7bwMnH61x-bklnQOhQud1Is6o_cn2wTj8EYDi9musrIT9z2MdAa$$" + # repair-root + strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: nttW6NfBXI_E-00u38W-KH6eiksfQNXuI7IiumoV49_zkbhM0sYTzSnFlwZC-W4t$$" endif ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) # run the snap-confine tests $(MAKE) -C cmd check endif -override_dh_systemd_enable: - # enable auto-import - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.autoimport.service - # we want the auto-update timer enabled by default - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.refresh.timer - # but the auto-update service disabled - dh_systemd_enable \ - --no-enable \ - -psnapd \ - data/systemd/snapd.refresh.service - # we want the repair timer enabled by default - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.snap-repair.timer - # but the repair service disabled - dh_systemd_enable \ - --no-enable \ - -psnapd \ - data/systemd/snapd.snap-repair.service - # enable snapd - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.socket - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.service - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.system-shutdown.service - dh_systemd_enable \ - -psnapd \ - data/systemd/snapd.core-fixup.service - -override_dh_systemd_start: - # we want to start the auto-update timer - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.refresh.timer - # but not start the service - dh_systemd_start \ - --no-start \ - -psnapd \ - data/systemd/snapd.refresh.service - # start snapd - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.socket - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.service - # start autoimport - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.autoimport.service - dh_systemd_start \ - -psnapd \ - data/systemd/snapd.core-fixup.service - override_dh_install: # we do not need this in the package, its just needed during build rm -rf ${CURDIR}/debian/tmp/usr/bin/xgettext-go diff -Nru snapd-2.28.5/packaging/ubuntu-17.04/snapd.dirs snapd-2.29.3/packaging/ubuntu-17.04/snapd.dirs --- snapd-2.28.5/packaging/ubuntu-17.04/snapd.dirs 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-17.04/snapd.dirs 2017-10-23 06:17:27.000000000 +0000 @@ -1,5 +1,7 @@ snap +var/cache/snapd usr/lib/snapd +var/lib/snapd/apparmor/snap-confine.d var/lib/snapd/auto-import var/lib/snapd/desktop var/lib/snapd/environment diff -Nru snapd-2.28.5/packaging/ubuntu-17.04/snapd.install snapd-2.29.3/packaging/ubuntu-17.04/snapd.install --- snapd-2.28.5/packaging/ubuntu-17.04/snapd.install 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-17.04/snapd.install 2017-10-23 06:17:27.000000000 +0000 @@ -24,7 +24,7 @@ lib/udev/snappy-app-dev usr/lib/snapd/snap-confine usr/lib/snapd/snap-discard-ns -usr/share/man/man5/snap-confine.5 +usr/share/man/man1/snap-confine.1 usr/share/man/man5/snap-discard-ns.5 # for compatibility with ancient snap installs that wrote the shell based # wrapper scripts instead of the modern symlinks diff -Nru snapd-2.28.5/packaging/ubuntu-17.04/snapd.postrm snapd-2.29.3/packaging/ubuntu-17.04/snapd.postrm --- snapd-2.28.5/packaging/ubuntu-17.04/snapd.postrm 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/packaging/ubuntu-17.04/snapd.postrm 2017-10-23 06:17:27.000000000 +0000 @@ -4,22 +4,30 @@ systemctl_stop() { unit="$1" - if systemctl is-active -q "$unit"; then - echo "Stoping $unit" + for i in $(seq 10); do + if ! systemctl is-active -q "$unit"; then + echo "$unit is stopped." + break + fi + echo "Stoping $unit [attempt $i]" systemctl stop -q "$unit" || true - fi + sleep .1 + done } if [ "$1" = "purge" ]; then # undo any bind mount to /snap that resulted from LP:#1668659 + # (that bug can't happen in trusty -- and doing this would mess up snap.mount.service there) if grep -q "/snap /snap" /proc/self/mountinfo; then umount -l /snap || true fi - mounts=$(systemctl list-unit-files --full | grep '^snap[-.].*\.mount' | cut -f1 -d ' ') - services=$(systemctl list-unit-files --full | grep '^snap[-.].*\.service' | cut -f1 -d ' ') + units=$(systemctl list-unit-files --full | grep '^snap[-.]' | cut -f1 -d ' ' | grep -vF snap.mount.service || true) + mounts=$(echo "$units" | grep '^snap[-.].*\.mount$' || true) + services=$(echo "$units" | grep '^snap[-.].*\.service$' || true) + for unit in $services $mounts; do - # ensure its really a snapp mount unit or systemd unit + # ensure its really a snap mount unit or systemd unit if ! grep -q 'What=/var/lib/snapd/snaps/' "/etc/systemd/system/$unit" && ! grep -q 'X-Snappy=yes' "/etc/systemd/system/$unit"; then echo "Skipping non-snapd systemd unit $unit" continue @@ -43,7 +51,8 @@ rm -f "/snap/bin/$snap" rm -f "/snap/bin/$snap".* # snap mount dir - umount -l "/snap/$snap/$rev" 2> /dev/null || true + # we pass -d (clean up loopback devices) for trusty compatibility + umount -d -l "/snap/$snap/$rev" 2> /dev/null || true rm -rf "/snap/$snap/$rev" rm -f "/snap/$snap/current" # snap data dir @@ -53,7 +62,7 @@ # opportunistic remove (may fail if there are still revisions left for d in "/snap/$snap" "/var/snap/$snap"; do if [ -d "$d" ]; then - rmdir --ignore-fail-on-non-empty "$d" + rmdir --ignore-fail-on-non-empty "$d" || true fi done fi @@ -74,12 +83,19 @@ # opportunistic as those might not be actually mounted for mnt in /run/snapd/ns/*.mnt; do umount -l "$mnt" || true + rm -f "$mnt" + done + for fstab in /run/snapd/ns/*.fstab; do + rm -f "$fstab" done umount -l /run/snapd/ns/ || true echo "Removing extra snap-confine apparmor rules" rm -f /etc/apparmor.d/snap.core.*.usr.lib.snapd.snap-confine + echo "Removing snapd cache" + rm -f /var/cache/snapd/* + echo "Removing snapd state" rm -rf /var/lib/snapd fi diff -Nru snapd-2.28.5/po/am.po snapd-2.29.3/po/am.po --- snapd-2.28.5/po/am.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/am.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Amharic translation for snapd +# Copyright (c) 2017 Rosetta Contributors and Canonical Ltd 2017 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-04-03 15:55+0000\n" +"Last-Translator: samson \n" +"Language-Team: Amharic \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "%s %s የተጫነው ከ %s\n" + +#, c-format +msgid "%s (delta)" +msgstr "%s (ዴልታ)" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "%s (በ sudo ይሞክሩ)" + +#, c-format +msgid "%s already installed\n" +msgstr "%s በቅድሚያ ተገጥሟል\n" + +#, c-format +msgid "%s disabled\n" +msgstr "%s ተሰናክሏል\n" + +#, c-format +msgid "%s enabled\n" +msgstr "%s ተችሏል\n" + +#, c-format +msgid "%s not installed\n" +msgstr "%s አልተገጠመም\n" + +#, c-format +msgid "%s removed\n" +msgstr "%s ተወግዷል\n" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "%s ተቀይሯል ወደ %s\n" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "%s%s %s ከ '%s' ተገጥሟል\n" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "%s%s %s ከ '%s' ተነቃቅቷል\n" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "%s%s %s ተገጥሟል\n" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "%s%s %s ተነቃቅቷል\n" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "<ሀሰት>" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "<ኢሜይል>" + +msgid "" +msgstr "<የ ፋይል ስም>" + +msgid "
" +msgstr "<የ ራስጌ ማጣሪያ filter>" + +msgid "" +msgstr "" + +msgid "" +msgstr "<ቁልፍ-ስም>" + +msgid "" +msgstr "<ቁልፍ>" + +msgid "" +msgstr "" + +msgid "" +msgstr "<ጥያቄ>" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "አማራጭ ትእዛዝ ለማስኬድ" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "ፋይል ማረጋገጫ" + +msgid "Assertion type name" +msgstr "የ ማረጋገጫ ስም አይነት" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "መጥፎ ኮድ: እባክዎን እንደገና ይሞክሩ: " + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "የ ማሰናጃ ምርጫ መቀየሪያ" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "ማረጋገጫ የማለፊያ ሀረግ: " + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "መገናኛ %s:%s ወደ %s:%s" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "መለያያ %s:%s ከ %s:%s" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "የ ኢሜይል አድራሻ: " + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "የሚገጠሙ ጥቅሎች ተገኝተዋል" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "ማምጫ ማስገደጃ በ ዘመናዊ ስርአቶች ውስጥ" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "የ አካል ቁልፍ ማመንጫ" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "እርዳታ" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "የ ተቀየሩ ስራዎች ዝርዝር" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "በ ስርአቱ ውስጥ ያሉ የ ሀሰት ዝርዝሮች" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "መግባቱ ተሳክቷል" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "የሚጠፋው የ ቁልፍ ስም" + +msgid "Name of key to export" +msgstr "የሚላከው የ ቁልፍ ስም" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "የ ማለፊያ ሀረግ: " + +#, c-format +msgid "Password of %q: " +msgstr "የ ማለፊያ ቃል ለ %q:amp> " + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "እትም ማተሚያ እና መውጫ" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "የ ተሰጠውን ክለሳ ብቻ ማስወገጃ" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "መፈለጊያውን በ ተሰጠው ክፍል መወሰኛ" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "የ እትም ዝርዝር ማሳያ" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "በርካታ ክርክሮች: %s" + +msgid "unavailable" +msgstr "ዝግጁ አይደለም" + +#, c-format +msgid "unknown attribute %q" +msgstr "ያልታወቀ መለያ %q" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/bs.po snapd-2.29.3/po/bs.po --- snapd-2.28.5/po/bs.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/bs.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Bosnian translation for snapd +# Copyright (c) 2017 Rosetta Contributors and Canonical Ltd 2017 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-09-08 10:15+0000\n" +"Last-Translator: Samir Ribić \n" +"Language-Team: Bosnian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "%s (vidi \"snap login --help\")" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "%s je već instaliran\n" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/ca.po snapd-2.29.3/po/ca.po --- snapd-2.28.5/po/ca.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/ca.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Catalan translation for snapd +# Copyright (c) 2017 Rosetta Contributors and Canonical Ltd 2017 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-03-18 12:27+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Catalan \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "%s %s muntat a %s\n" + +#, c-format +msgid "%s (delta)" +msgstr "%s (delta)" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "%s (veieu \"snap login --help\")" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "%s (proveu amb sudo)" + +#, c-format +msgid "%s already installed\n" +msgstr "%s ja està instal·lat\n" + +#, c-format +msgid "%s disabled\n" +msgstr "%s desactivat\n" + +#, c-format +msgid "%s enabled\n" +msgstr "%s activat\n" + +#, c-format +msgid "%s not installed\n" +msgstr "%s no està instal·lat\n" + +#, c-format +msgid "%s removed\n" +msgstr "S'ha suprimit %s\n" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "%s s'ha restituït a %s\n" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "S'ha instal·lat %s%s %s de '%s'\n" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "S'ha actualitzat %s%s %s de '%s'\n" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "S'ha instal·lat %s%s %s\n" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "S'ha actualitzat %s%s %s\n" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "-r només es pot utilitzar amb --hook" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "Avorta un canvi pendent" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "Afegeix una afirmació al sistema" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "Alias de --dangerous" + +msgid "All snaps up to date." +msgstr "All snaps up to date." + +msgid "Alternative command to run" +msgstr "Ordre alternativa que s'executarà" + +msgid "Always return document, even with single key" +msgstr "Torna sempre el document, inclús amb una sola clau" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "Un correu electrònic d'un usuari a login.ubuntu.com" + +msgid "Assertion file" +msgstr "Fitxer de definició" + +msgid "Assertion type name" +msgstr "Nom del tipus de definició" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "El codi és incorrecte. Torneu a provar: " + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "Canvia l'identificador" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/cs.po snapd-2.29.3/po/cs.po --- snapd-2.28.5/po/cs.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/cs.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1813 @@ +# Czech translation for snapd +# Copyright (c) 2017 Rosetta Contributors and Canonical Ltd 2017 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-06-21 17:43+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Czech \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "Nápověda" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "Vypsat informace o verzi a skončit" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" +"\n" +"Instalace, konfigurace, obnovení a odstranění snap balíčků. Snapy jsou\n" +"„univerzální“ balíčky, které fungují napříč mnoha různými linuxovými " +"systémy,\n" +"umožňující bezpečnou distribuci nejnovějších aplikací a nástrojů pro\n" +"cloud, servery, stolní počítače a internet věcí.\n" +"\n" +"Toto je CLI pro snapd, službu běžící na pozadí, která se stará o\n" +"snapy na tomto systému. Pro zobrazení nainstalovaných snapů použijte „snap " +"list“.\n" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/da.po snapd-2.29.3/po/da.po --- snapd-2.28.5/po/da.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/da.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Danish translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2016-09-22 21:20+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Danish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "%s (delta)" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/de.po snapd-2.29.3/po/de.po --- snapd-2.28.5/po/de.po 2017-01-12 09:32:51.000000000 +0000 +++ snapd-2.29.3/po/de.po 2017-10-27 12:23:38.000000000 +0000 @@ -6,106 +6,133 @@ msgid "" msgstr "" "Project-Id-Version: snappy\n" -"Report-Msgid-Bugs-To: snappy-devel@lists.ubuntu.com\n" -"POT-Creation-Date: 2016-12-14 12:38+0100\n" -"PO-Revision-Date: 2015-09-02 22:57+0000\n" -"Last-Translator: FULL NAME \n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-01-07 18:59+0000\n" +"Last-Translator: Tobias Bannert \n" "Language-Team: German \n" -"Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2015-10-21 06:21+0000\n" -"X-Generator: Launchpad (build 17812)\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" #. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). #, c-format msgid "%s %s mounted from %s\n" +msgstr "%s %s zusammengebaut aus %s\n" + +#, c-format +msgid "%s (delta)" msgstr "" +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) #, c-format msgid "%s (see \"snap login --help\")" -msgstr "" +msgstr "%s (siehe »snap login --help«)" -#. TRANSLATORS: %s will be a message along the lines of "login required" +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) #, c-format msgid "%s (try with sudo)" -msgstr "" +msgstr "%s (bitte mit sudo versuchen)" #, c-format msgid "%s already installed\n" -msgstr "" +msgstr "%s ist bereits installiert\n" #, c-format msgid "%s disabled\n" -msgstr "" +msgstr "%s deaktiviert\n" #, c-format msgid "%s enabled\n" -msgstr "" +msgstr "%s aktiviert\n" #, c-format msgid "%s not installed\n" -msgstr "" +msgstr "%s ist nicht installiert\n" #, c-format msgid "%s removed\n" -msgstr "" +msgstr "%s entfernt\n" #, c-format msgid "%s reverted to %s\n" -msgstr "" +msgstr "%s zurückgesetzt zu %s\n" #, c-format msgid "%s%s %s from '%s' installed\n" -msgstr "" +msgstr "%s %s %s aus »%s« installiert\n" #, c-format -msgid "%s%s %s from '%s' upgraded\n" -msgstr "" +msgid "%s%s %s from '%s' refreshed\n" +msgstr "%s %s %s von »%s« aufgefrischt\n" #, c-format msgid "%s%s %s installed\n" -msgstr "" +msgstr "%s%s %s installiert\n" #, c-format -msgid "%s%s %s upgraded\n" -msgstr "" +msgid "%s%s %s refreshed\n" +msgstr "%s %s %s aufgefrischt\n" msgid "--list does not take mode nor channel flags" +msgstr "--list nimmt weder Modus- noch Kanalflaggen an" + +msgid "--time does not take mode nor channel flags" msgstr "" msgid "-r can only be used with --hook" +msgstr "-r kann nur mit --hook benutzt werden" + +msgid "" msgstr "" +msgid "" +msgstr "" + msgid "" -msgstr "" +msgstr "" msgid "" -msgstr "" +msgstr "" msgid "" msgstr "" msgid "" -msgstr "" +msgstr "" #. TRANSLATORS: noun #. TRANSLATORS: noun msgid "" -msgstr "" +msgstr "" msgid "" -msgstr "" +msgstr "" msgid "
" +msgstr "" + +msgid "" msgstr "" msgid "" -msgstr "" +msgstr "" msgid "" -msgstr "" +msgstr "" msgid "" msgstr "" @@ -128,6 +155,9 @@ msgid "Abort a pending change" msgstr "" +msgid "Added" +msgstr "" + msgid "Adds an assertion to the system" msgstr "" @@ -135,7 +165,7 @@ msgstr "" msgid "All snaps up to date." -msgstr "" +msgstr "Alle Snaps sind aktuell" msgid "Alternative command to run" msgstr "" @@ -156,6 +186,19 @@ msgid "Authenticates on snapd and the store" msgstr "" +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + msgid "Bad code. Try again: " msgstr "" @@ -174,8 +217,7 @@ "it." msgstr "" -#, c-format -msgid "Clear alias state for snap %q" +msgid "Command\tAlias\tNotes" msgstr "" msgid "Configuration value (key=value)" @@ -230,6 +272,14 @@ msgid "Disable %q snap" msgstr "" +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + msgid "Disables a snap in the system" msgstr "" @@ -244,6 +294,9 @@ msgid "Disconnects a plug from a slot" msgstr "" +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + #, c-format msgid "Download snap %q%s from channel %q" msgstr "" @@ -257,16 +310,12 @@ msgstr "" msgid "Email address: " -msgstr "" +msgstr "E-Mail-Adresse: " #, c-format msgid "Enable %q snap" msgstr "" -#, c-format -msgid "Enable aliases for snap %q" -msgstr "" - msgid "Enables a snap in the system" msgstr "" @@ -322,7 +371,7 @@ msgstr "" msgid "Help" -msgstr "" +msgstr "Hilfe" msgid "Hook to run" msgstr "" @@ -330,9 +379,6 @@ msgid "ID\tStatus\tSpawn\tReady\tSummary\n" msgstr "" -msgid "ISO 4217 code for currency (https://en.wikipedia.org/wiki/ISO_4217)" -msgstr "" - msgid "Identifier of the signer" msgstr "" @@ -342,9 +388,20 @@ msgid "Ignore validation by other snaps blocking the refresh" msgstr "" +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" msgstr "" +msgid "Include unused interfaces" +msgstr "" + msgid "Initialize device" msgstr "" @@ -398,6 +455,9 @@ "implies this)" msgstr "" +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + msgid "Installs a snap to the system" msgstr "" @@ -414,19 +474,25 @@ msgstr "" msgid "List installed snaps" -msgstr "" +msgstr "Installierte Snaps auflisten" msgid "List system changes" +msgstr "Systemänderungen auflisten" + +msgid "Lists aliases in the system" msgstr "" msgid "Lists interfaces in the system" msgstr "" +msgid "Lists snap interfaces" +msgstr "" + msgid "Log out of the store" msgstr "" msgid "Login successful" -msgstr "" +msgstr "Anmeldung erfolgreich" #, c-format msgid "Make current revision for snap %q unavailable" @@ -473,6 +539,9 @@ msgid "Name\tSHA3-384" msgstr "" +msgid "Name\tSummary" +msgstr "" + msgid "Name\tVersion\tDeveloper\tNotes\tSummary" msgstr "" @@ -482,6 +551,9 @@ msgid "No snaps are installed yet. Try \"snap install hello-world\"." msgstr "" +msgid "No snaps to auto-refresh found" +msgstr "" + msgid "Output results in JSON format" msgstr "" @@ -500,10 +572,14 @@ msgstr "" #, c-format -msgid "" -"Please visit https://my.ubuntu.com/payment/edit to agree to the latest terms " -"and conditions.\n" -"Once completed, return here and run 'snap buy %s' again." +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" msgstr "" msgid "Prepare a snappy image" @@ -523,9 +599,19 @@ msgid "Prints configuration options" msgstr "" +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + msgid "Prints whether system is managed" msgstr "" +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + msgid "Put snap in classic mode and disable security confinement" msgstr "" @@ -535,6 +621,9 @@ msgid "Put snap in enforced confinement mode" msgstr "" +msgid "Query the status of services" +msgstr "" + #, c-format msgid "Refresh %q snap" msgstr "" @@ -543,6 +632,10 @@ msgid "Refresh %q snap from %q channel" msgstr "" +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + msgid "Refresh all snaps: no updates" msgstr "" @@ -555,6 +648,11 @@ msgid "Refresh snaps %s" msgstr "" +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + msgid "Refresh to the given revision" msgstr "" @@ -573,6 +671,10 @@ msgid "Remove data for snap %q (%s)" msgstr "" +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + msgid "Remove only the given revision" msgstr "" @@ -581,6 +683,10 @@ msgstr "" #, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format msgid "Remove snap %q" msgstr "" @@ -593,15 +699,27 @@ msgid "Remove snaps %s" msgstr "" +msgid "Removed" +msgstr "" + msgid "Removes a snap from the system" msgstr "" msgid "Request device serial" msgstr "" +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + msgid "Restrict the search to a given section" msgstr "" +msgid "Retrieve logs of services" +msgstr "" + #, c-format msgid "Revert %q snap" msgstr "" @@ -620,21 +738,61 @@ msgid "Run configure hook of %q snap if present" msgstr "" +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + msgid "Run prepare-device hook" msgstr "" +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + msgid "Run the given snap command" msgstr "" msgid "Run the given snap command with the right confinement and environment" msgstr "" -msgid "Runs unsupported experimental commands" +msgid "Runs debug commands" msgstr "" msgid "Search private snaps" msgstr "" +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + #, c-format msgid "Setup snap %q aliases" msgstr "" @@ -643,10 +801,23 @@ msgid "Setup snap %q%s security profiles" msgstr "" +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + msgid "Show all revisions" msgstr "" -msgid "Show available snaps for refresh" +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" msgstr "" msgid "Shows known assertions of the provided type" @@ -670,12 +841,18 @@ msgid "Snap name" msgstr "" +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + msgid "" "Sorry, your payment method has been declined by the issuer. Please review " "your\n" "payment details at https://my.ubuntu.com/payment/edit and try again." msgstr "" +msgid "Start services" +msgstr "" + #, c-format msgid "Start snap %q (%s) services" msgstr "" @@ -687,9 +864,18 @@ msgid "Start snap services" msgstr "" +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + msgid "Status\tSpawn\tReady\tSummary\n" msgstr "" +msgid "Stop services" +msgstr "" + #, c-format msgid "Stop snap %q (%s) services" msgstr "" @@ -701,9 +887,27 @@ msgid "Stop snap services" msgstr "" +msgid "Stopped.\n" +msgstr "" + msgid "Strict typing with nulls and quoted strings" msgstr "" +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + msgid "Temporarily mount device before inspecting" msgstr "" @@ -717,6 +921,10 @@ "with 'snap install %s'." msgstr "" +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + msgid "The login.ubuntu.com email to login as" msgstr "" @@ -726,12 +934,20 @@ msgid "The output directory" msgstr "" +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + msgid "The snap to configure (e.g. hello-world)" msgstr "" msgid "The snap whose conf is being requested" msgstr "" +msgid "The userd command starts the snap user session service." +msgstr "" + msgid "This command logs the current user out of the store" msgstr "" @@ -739,12 +955,22 @@ msgstr "" #, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format msgid "Try %q snap from %s" msgstr "" msgid "Two-factor code: " msgstr "" +msgid "Unalias a manual alias or an entire snap" +msgstr "" + msgid "Use a specific snap revision when running hook" msgstr "" @@ -767,24 +993,30 @@ msgid "Wrong again. Once more: " msgstr "" +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + msgid "Yes, yes it does." msgstr "Ja, ja, allerdings." -#, c-format msgid "" -"You do not have a payment method associated with your account, visit https://" -"my.ubuntu.com/payment/edit to add one.\n" -"Once completed, return here and run 'snap buy %s' again." +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." msgstr "" +#, c-format msgid "" -"You need to be logged in to purchase software. Please run 'snap login' and " -"try again." +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." msgstr "" #. TRANSLATORS: the %s is the argument given by the user to "snap changes" #, c-format -msgid "\"snap changes\" command expects a snap name, try: \"snap change %s\"" +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" msgstr "" msgid "" @@ -818,6 +1050,29 @@ msgid "" "\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" "The auto-import command searches available mounted devices looking for\n" "assertions that are signed by trusted authorities, and potentially\n" "performs system changes based on them.\n" @@ -837,14 +1092,15 @@ msgid "" "\n" -"The change command displays a summary of tasks associated to an individual " -"change." +"The changes command displays a summary of the recent system changes " +"performed." msgstr "" msgid "" "\n" -"The changes command displays a summary of the recent system changes " -"performed." +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" msgstr "" msgid "" @@ -883,6 +1139,14 @@ msgid "" "\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" "The disable command disables a snap. The binaries and services of the\n" "snap will no longer be available. But all the data is still available\n" "and the snap can easily be enabled again.\n" @@ -917,15 +1181,8 @@ msgid "" "\n" -"The experimental command contains a selection of additional sub-commands.\n" -"\n" -"Experimental commands can be removed without notice and may not work on\n" -"non-development systems.\n" -msgstr "" - -msgid "" -"\n" -"The find command queries the store for available packages.\n" +"The find command queries the store for available packages in the stable " +"channel.\n" msgstr "" msgid "" @@ -947,6 +1204,23 @@ "\n" " $ snapctl get author.name\n" " frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" msgstr "" msgid "" @@ -988,6 +1262,14 @@ msgid "" "\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" "The interfaces command lists interfaces available in the system.\n" "\n" "By default all slots and plugs, used and offered by all snaps, are " @@ -1040,6 +1322,31 @@ msgid "" "\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" "The refresh command refreshes (updates) the named snap.\n" msgstr "" @@ -1090,6 +1397,24 @@ "Nested values may be modified via a dotted path:\n" "\n" " $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." msgstr "" msgid "" @@ -1110,6 +1435,12 @@ msgid "" "\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" "The version command displays the versions of the running client, server,\n" "and operating system.\n" msgstr "" @@ -1123,6 +1454,24 @@ msgid "" "\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" "\n" "The home directory is shared between snappy and the classic dimension.\n" "Run \"exit\" to leave the classic shell.\n" @@ -1137,6 +1486,9 @@ msgid "a single snap name must be specified when ignoring validation" msgstr "" +msgid "active" +msgstr "" + msgid "bought" msgstr "" @@ -1188,6 +1540,10 @@ msgstr "" #, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format msgid "cannot get the current user: %v" msgstr "" @@ -1203,6 +1559,11 @@ msgid "cannot read assertion input: %v" msgstr "" +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + #, c-format msgid "cannot resolve snap app %q: %v" msgstr "" @@ -1211,6 +1572,10 @@ msgid "cannot sign assertion: %v" msgstr "" +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + #. TRANSLATORS: %q is the key name, %v the error message #, c-format msgid "cannot use %q key: %v" @@ -1219,14 +1584,30 @@ msgid "cannot use --hook and --command together" msgstr "" +msgid "cannot use change ID and type together" +msgstr "" + msgid "cannot use devmode and jailmode flags together" msgstr "" #, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format msgid "change finished in status %q with no error message" msgstr "" #, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format msgid "created user %q\n" msgstr "" @@ -1234,18 +1615,53 @@ msgid "disabled" msgstr "" +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + #, c-format msgid "error: %v\n" -msgstr "" +msgstr "Fehler: %v\n" msgid "" "error: the `` argument was not provided and couldn't be inferred" msgstr "" +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + #, c-format msgid "internal error, please report: running %q failed: %v\n" msgstr "" +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + #, c-format msgid "invalid attribute: %q (want key=value)" msgstr "" @@ -1271,44 +1687,88 @@ "key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" msgstr "" +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + msgid "need the application to run as argument" msgstr "" msgid "no changes found" +msgstr "keine Änderungen gefunden" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" msgstr "" msgid "no interfaces found" msgstr "" msgid "no matching snaps installed" -msgstr "" +msgstr "keine passenden Snaps installiert" -#. TRANSLATORS: the %q is the (quoted) query the user entered -#, c-format -msgid "no snaps found for %q" +msgid "no such interface" msgstr "" msgid "no valid snaps given" -msgstr "" +msgstr "keine gültigen Snaps angegeben" msgid "not a valid snap" msgstr "" +msgid "please provide change ID or type with --last=" +msgstr "" + #. TRANSLATORS: if possible, a single short word msgid "private" -msgstr "" +msgstr "privat" msgid "" "reboot scheduled to update the system - temporarily cancel with 'sudo " "shutdown -c'" msgstr "" +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + msgid "show detailed information about a snap" msgstr "" +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + #. TRANSLATORS: free as in gratis msgid "snap is free" -msgstr "" +msgstr "Snap ist kostenlos" msgid "too many arguments for command" msgstr "" @@ -1324,12 +1784,20 @@ msgstr "" msgid "unavailable" +msgstr "nicht verfügbar" + +#, c-format +msgid "unknown attribute %q" msgstr "" #, c-format msgid "unknown command %q, see \"snap --help\"" +msgstr "unbekannter Befehl %q, siehe »snap --help«" + +#, c-format +msgid "unknown plug or slot %q" msgstr "" #, c-format msgid "unsupported shell %v" -msgstr "" +msgstr "nicht unterstützte Shell %v" diff -Nru snapd-2.28.5/po/el.po snapd-2.29.3/po/el.po --- snapd-2.28.5/po/el.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/el.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Greek translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2016-09-04 11:48+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Greek \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "Επιτυχής σύνδεση" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/en_GB.po snapd-2.29.3/po/en_GB.po --- snapd-2.28.5/po/en_GB.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/en_GB.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,2206 @@ +# English (United Kingdom) translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-10-14 10:22+0000\n" +"Last-Translator: Anthony Harrington \n" +"Language-Team: English (United Kingdom) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "%q switched to the %q channel\n" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "%s %s mounted from %s\n" + +#, c-format +msgid "%s (delta)" +msgstr "%s (delta)" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "%s (see \"snap login --help\")" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "%s (try with sudo)" + +#, c-format +msgid "%s already installed\n" +msgstr "%s already installed\n" + +#, c-format +msgid "%s disabled\n" +msgstr "%s disabled\n" + +#, c-format +msgid "%s enabled\n" +msgstr "%s enabled\n" + +#, c-format +msgid "%s not installed\n" +msgstr "%s not installed\n" + +#, c-format +msgid "%s removed\n" +msgstr "%s removed\n" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "%s reverted to %s\n" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "%s%s %s from '%s' installed\n" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "%s%s %s from '%s' refreshed\n" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "%s%s %s installed\n" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "%s%s %s refreshed\n" + +msgid "--list does not take mode nor channel flags" +msgstr "--list does not take mode nor channel flags" + +msgid "--time does not take mode nor channel flags" +msgstr "--time does not take mode nor channel flags" + +msgid "-r can only be used with --hook" +msgstr "-r can only be used with --hook" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "
" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr ":" + +msgid ":" +msgstr ":" + +msgid ":" +msgstr ":" + +msgid "Abort a pending change" +msgstr "Abort a pending change" + +msgid "Added" +msgstr "Added" + +msgid "Adds an assertion to the system" +msgstr "Adds an assertion to the system" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "Alias for --dangerous (DEPRECATED)" + +msgid "All snaps up to date." +msgstr "All snaps up to date." + +msgid "Alternative command to run" +msgstr "Alternative command to run" + +msgid "Always return document, even with single key" +msgstr "Always return document, even with single key" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "An e-mail of a user on login.ubuntu.com" + +msgid "Assertion file" +msgstr "Assertion file" + +msgid "Assertion type name" +msgstr "Assertion type name" + +msgid "Authenticates on snapd and the store" +msgstr "Authenticates on snapd and the store" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "Auto-refresh %d snaps" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "Auto-refresh snap %q" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "Auto-refresh snaps %s" + +msgid "Bad code. Try again: " +msgstr "Bad code. Try again: " + +msgid "Buys a snap" +msgstr "Buys a snap" + +msgid "Change ID" +msgstr "Change ID" + +msgid "Changes configuration options" +msgstr "Changes configuration options" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." + +msgid "Command\tAlias\tNotes" +msgstr "Command\tAlias\tNotes" + +msgid "Configuration value (key=value)" +msgstr "Configuration value (key=value)" + +msgid "Confirm passphrase: " +msgstr "Confirm passphrase: " + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "Connect %s:%s to %s:%s" + +msgid "Connects a plug to a slot" +msgstr "Connects a plug to a slot" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "Constrain listing to a specific snap or snap:name" + +msgid "Constrain listing to specific interfaces" +msgstr "Constrain listing to specific interfaces" + +msgid "Constrain listing to those matching header=value" +msgstr "Constrain listing to those matching header=value" + +#, c-format +msgid "Copy snap %q data" +msgstr "Copy snap %q data" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" +"Create a cryptographic key pair that can be used for signing assertions." + +msgid "Create cryptographic key pair" +msgstr "Create cryptographic key pair" + +msgid "Create snap build assertion" +msgstr "Create snap build assertion" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "Create snap-build assertion for the provided snap file." + +msgid "Creates a local system user" +msgstr "Creates a local system user" + +msgid "Delete cryptographic key pair" +msgstr "Delete cryptographic key pair" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "Delete the local cryptographic key pair with the given name." + +#, c-format +msgid "Disable %q snap" +msgstr "Disable %q snap" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "Disable aliases for snap %q" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "Disable all aliases for snap %q" + +msgid "Disables a snap in the system" +msgstr "Disables a snap in the system" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "Discard interface connections for snap %q (%s)" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "Disconnect %s:%s from %s:%s" + +msgid "Disconnects a plug from a slot" +msgstr "Disconnects a plug from a slot" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" +"Do not wait for the operation to finish but just print the change id." + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "Download snap %q%s from channel %q" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" +"Download the given revision of a snap, to which you must have developer " +"access" + +msgid "Downloads the given snap" +msgstr "Downloads the given snap" + +msgid "Email address: " +msgstr "E-mail address: " + +#, c-format +msgid "Enable %q snap" +msgstr "Enable %q snap" + +msgid "Enables a snap in the system" +msgstr "Enables a snap in the system" + +msgid "Entering classic dimension" +msgstr "Entering classic dimension" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" +"Export a public key assertion body that may be imported by other systems." + +msgid "Export cryptographic public key" +msgstr "Export cryptographic public key" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "Fetch and check assertions for snap %q%s" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "Fetching assertions for %q\n" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "Fetching snap %q\n" + +msgid "Filename of the snap you want to assert a build for" +msgstr "Filename of the snap you want to assert a build for" + +msgid "Finds packages to install" +msgstr "Finds packages to install" + +msgid "Force adding the user, even if the device is already managed" +msgstr "Force adding the user, even if the device is already managed" + +msgid "Force import on classic systems" +msgstr "Force import on classic systems" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" +"Format public key material as a request for an account-key for this account-" +"id" + +msgid "Generate device key" +msgstr "Generate device key" + +msgid "Generate the manpage" +msgstr "Generate the manpage" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "Grade states the build quality of the snap (defaults to 'stable')" + +msgid "Grant sudo access to the created user" +msgstr "Grant sudo access to the created user" + +msgid "Help" +msgstr "Help" + +msgid "Hook to run" +msgstr "Hook to run" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "ID\tStatus\tSpawn\tReady\tSummary\n" + +msgid "Identifier of the signer" +msgstr "Identifier of the signer" + +msgid "Identifier of the snap package associated with the build" +msgstr "Identifier of the snap package associated with the build" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "Ignore validation by other snaps blocking the refresh" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" +"Include a verbose list of a snap's notes (otherwise, summarise notes)" + +msgid "Include unused interfaces" +msgstr "Include unused interfaces" + +msgid "Initialize device" +msgstr "Initialise device" + +msgid "Inspects devices for actionable information" +msgstr "Inspects devices for actionable information" + +#, c-format +msgid "Install %q snap" +msgstr "Install %q snap" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "Install %q snap from %q channel" + +#, c-format +msgid "Install %q snap from file" +msgstr "Install %q snap from file" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "Install %q snap from file %q" + +msgid "Install from the beta channel" +msgstr "Install from the beta channel" + +msgid "Install from the candidate channel" +msgstr "Install from the candidate channel" + +msgid "Install from the edge channel" +msgstr "Install from the edge channel" + +msgid "Install from the stable channel" +msgstr "Install from the stable channel" + +#, c-format +msgid "Install snap %q" +msgstr "Install snap %q" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "Install snaps %s" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" +"Install the given revision of a snap, to which you must have developer access" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "Install the given snap without enabling its automatic aliases" + +msgid "Installs a snap to the system" +msgstr "Installs a snap to the system" + +msgid "Key of interest within the configuration" +msgstr "Key of interest within the configuration" + +msgid "List a change's tasks" +msgstr "List a change's tasks" + +msgid "List cryptographic keys" +msgstr "List cryptographic keys" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "List cryptographic keys that can be used for signing assertions." + +msgid "List installed snaps" +msgstr "List installed snaps" + +msgid "List system changes" +msgstr "List system changes" + +msgid "Lists aliases in the system" +msgstr "Lists aliases in the system" + +msgid "Lists interfaces in the system" +msgstr "Lists interfaces in the system" + +msgid "Lists snap interfaces" +msgstr "Lists snap interfaces" + +msgid "Log out of the store" +msgstr "Log out of the store" + +msgid "Login successful" +msgstr "Login successful" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "Make current revision for snap %q unavailable" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "Make snap %q (%s) available to the system" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "Make snap %q (%s) unavailable to the system" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "Make snap %q unavailable to the system" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "Make snap %q%s available to the system" + +msgid "Mark system seeded" +msgstr "Mark system seeded" + +#, c-format +msgid "Mount snap %q%s" +msgstr "Mount snap %q%s" + +msgid "Name of key to create; defaults to 'default'" +msgstr "Name of key to create; defaults to 'default'" + +msgid "Name of key to delete" +msgstr "Name of key to be deleted" + +msgid "Name of key to export" +msgstr "Name of key to export" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "Name of the GnuPG key to use (defaults to 'default' as key name)" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "Name of the key to use, otherwise use the default key" + +msgid "Name\tSHA3-384" +msgstr "Name\tSHA3-384" + +msgid "Name\tSummary" +msgstr "Name\tSummary" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "Name\tVersion\tDeveloper\tNotes\tSummary" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "Name\tVersion\tRev\tDeveloper\tNotes" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "No snaps are installed yet. Try \"snap install hello-world\"." + +msgid "No snaps to auto-refresh found" +msgstr "No snaps to auto-refresh found" + +msgid "Output results in JSON format" +msgstr "Output results in JSON format" + +msgid "Passphrase: " +msgstr "Passphrase: " + +#, c-format +msgid "Password of %q: " +msgstr "Password of %q: " + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "Prefer aliases for snap %q" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "Prefer aliases from a snap and disable conflicts" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "Prefer aliases of snap %q" + +msgid "Prepare a snappy image" +msgstr "Prepare a snappy image" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "Prepare snap %q (%s)" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "Prepare snap %q%s" + +msgid "Print the version and exit" +msgstr "Print the version, and exit" + +msgid "Prints configuration options" +msgstr "Prints configuration options" + +msgid "Prints the confinement mode the system operates in" +msgstr "Prints the confinement mode the system operates in" + +msgid "Prints the email the user is logged in with." +msgstr "Prints the e-mail the user is logged in with." + +msgid "Prints whether system is managed" +msgstr "Prints whether system is managed" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "Prune automatic aliases for snap %q" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "Put snap in classic mode and disable security confinement" + +msgid "Put snap in development mode and disable security confinement" +msgstr "Put snap in development mode and disable security confinement" + +msgid "Put snap in enforced confinement mode" +msgstr "Put snap in enforced confinement mode" + +msgid "Query the status of services" +msgstr "Query the status of services" + +#, c-format +msgid "Refresh %q snap" +msgstr "Refresh %q snap" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "Refresh %q snap from %q channel" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "Refresh aliases for snap %q" + +msgid "Refresh all snaps: no updates" +msgstr "Refresh all snaps: no updates" + +#, c-format +msgid "Refresh snap %q" +msgstr "Refresh snap %q" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "Refresh snaps %s" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "Refresh snaps %s: no updates" + +msgid "Refresh to the given revision" +msgstr "Refresh to the given revision" + +msgid "Refreshes a snap in the system" +msgstr "Refreshes a snap in the system" + +#, c-format +msgid "Remove %q snap" +msgstr "Remove %q snap" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "Remove aliases for snap %q" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "Remove data for snap %q (%s)" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "Remove manual alias %q for snap %q" + +msgid "Remove only the given revision" +msgstr "Remove only the given revision" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "Remove security profile for snap %q (%s)" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "Remove security profiles of snap %q" + +#, c-format +msgid "Remove snap %q" +msgstr "Remove snap %q" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "Remove snap %q (%s) from the system" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "Remove snaps %s" + +msgid "Removed" +msgstr "Removed" + +msgid "Removes a snap from the system" +msgstr "Removes a snap from the system" + +msgid "Request device serial" +msgstr "Request device serial" + +msgid "Restart services" +msgstr "Restart services" + +msgid "Restarted.\n" +msgstr "Restarted.\n" + +msgid "Restrict the search to a given section" +msgstr "Restrict the search to a given section" + +msgid "Retrieve logs of services" +msgstr "Retrieve logs of services" + +#, c-format +msgid "Revert %q snap" +msgstr "Revert %q snap" + +msgid "Reverts the given snap to the previous state" +msgstr "Reverts the given snap to the previous state" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "Run a shell instead of the command (useful for debugging)" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "Run configure hook of %q snap" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "Run configure hook of %q snap if present" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "Run hook %s of snap %q" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "Run install hook of %q snap if present" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "Run post-refresh hook of %q snap if present" + +msgid "Run prepare-device hook" +msgstr "Run prepare-device hook" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "Run remove hook of %q snap if present" + +msgid "Run the given snap command" +msgstr "Run the given snap command" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" +"Run the given snap command with the right confinement and environment" + +msgid "Runs debug commands" +msgstr "Runs debug commands" + +msgid "Search private snaps" +msgstr "Search private snaps" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "Set automatic aliases for snap %q" + +msgid "Sets up a manual alias" +msgstr "Sets up a manual alias" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "Setup alias %q => %q for snap %q" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "Setup manual alias %q => %q for snap %q" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "Setup snap %q (%s) security profiles" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "Setup snap %q aliases" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "Setup snap %q%s security profiles" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "Setup snap %q%s security profiles (phase 2)" + +msgid "Show all revisions" +msgstr "Show all revisions" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "Show auto refresh information but do not perform a refresh" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "Show available snaps for refresh but do not perform a refresh" + +msgid "Show details of a specific interface" +msgstr "Show details of a specific interface" + +msgid "Show interface attributes" +msgstr "Show interface attributes" + +msgid "Shows known assertions of the provided type" +msgstr "Shows known assertions of the provided type" + +msgid "Shows version details" +msgstr "Shows version details" + +msgid "Sign an assertion" +msgstr "Sign an assertion" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" + +msgid "Slot\tPlug" +msgstr "Slot\tPlug" + +msgid "Snap name" +msgstr "Snap name" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "Snap\tService\tStartup\tCurrent" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." + +msgid "Start services" +msgstr "Start services" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "Start snap %q (%s) services" + +#, c-format +msgid "Start snap %q%s services" +msgstr "Start snap %q%s services" + +msgid "Start snap services" +msgstr "Start snap services" + +msgid "Start the userd service" +msgstr "Start the userd service" + +msgid "Started.\n" +msgstr "Started.\n" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "Status\tSpawn\tReady\tSummary\n" + +msgid "Stop services" +msgstr "Stop services" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "Stop snap %q (%s) services" + +#, c-format +msgid "Stop snap %q services" +msgstr "Stop snap %q services" + +msgid "Stop snap services" +msgstr "Stop snap services" + +msgid "Stopped.\n" +msgstr "Stopped.\n" + +msgid "Strict typing with nulls and quoted strings" +msgstr "Strict typing with nulls and quoted strings" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "Switch %q snap to %s" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "Switch snap %q from %s to %s" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "Switch snap %q to %s" + +msgid "Switches snap to a different channel" +msgstr "Switches snap to a different channel" + +msgid "Temporarily mount device before inspecting" +msgstr "Temporarily mount device before inspecting" + +msgid "Tests a snap in the system" +msgstr "Tests a snap in the system" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" +"The get command prints configuration and interface connection settings." + +msgid "The login.ubuntu.com email to login as" +msgstr "The login.ubuntu.com e-mail to login as" + +msgid "The model assertion name" +msgstr "The model assertion name" + +msgid "The output directory" +msgstr "The output directory" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "The search %q returned 0 snaps\n" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "The snap to configure (e.g. hello-world)" + +msgid "The snap whose conf is being requested" +msgstr "The snap whose conf is being requested" + +msgid "The userd command starts the snap user session service." +msgstr "The userd command starts the snap user session service." + +msgid "This command logs the current user out of the store" +msgstr "This command logs the current user out of the store" + +msgid "Tool to interact with snaps" +msgstr "Tool to interact with snaps" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "Transition security profiles from %q to %q" + +msgid "Transition ubuntu-core to core" +msgstr "Transition ubuntu-core to core" + +#, c-format +msgid "Try %q snap from %s" +msgstr "Try %q snap from %s" + +msgid "Two-factor code: " +msgstr "Two-factor code: " + +msgid "Unalias a manual alias or an entire snap" +msgstr "Unalias a manual alias or an entire snap" + +msgid "Use a specific snap revision when running hook" +msgstr "Use a specific snap revision when running hook" + +msgid "Use known assertions for user creation" +msgstr "Use known assertions for user creation" + +msgid "Use this channel instead of stable" +msgstr "Use this channel instead of stable" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "WARNING: failed to activate logging: %v\n" + +msgid "Waiting for server to restart" +msgstr "Waiting for server to restart" + +msgid "Watch a change in progress" +msgstr "Watch a change in progress" + +msgid "Wrong again. Once more: " +msgstr "Wrong again. Once more: " + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "Xauthority file isn't owned by the current user %s" + +msgid "Yes, yes it does." +msgstr "Yes, yes it does." + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed, the assertion must be valid; its signature verified with a " +"known\n" +"public key; and the assertion consistent with and its prerequisite in the\n" +"database.\n" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is set up, the respective application command can be " +"invoked just using the alias.\n" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status(es).\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as 'undefined' means it was explicitly enabled or disabled " +"but is\n" +"not defined in the current revision of the snap; possibly temporarily (e.g\n" +"because of a revert). If not, this can be cleared with snap alias --reset.\n" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" +"\n" +"The auto-import command searches available mounted devices, looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case, the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" +"\n" +"The buy command buys a snap from the store.\n" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" +"\n" +"The enable command enables a snap that was previously disabled.\n" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" +"\n" +"The install command installs the named snap onto the system.\n" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" +"\n" +"The list command displays a summary of snaps installed on the current system." + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence, this snap will not refresh automatically " +"and may\n" +"perform arbitrary system changes outside of the security sandbox which snaps " +"are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed, repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement,\n" +"repeat the command including --jailmode." + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" +"\n" +"The unalias command tears down a manual alias when given one, or disables " +"all aliases of a snap, removing also all manual ones, when given a snap " +"name.\n" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" +"\n" +"The whoami command prints the e-mail the user is logged in with.\n" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed, repeat the command including --" +"classic.\n" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "a single snap name is needed to specify mode or channel flags" + +msgid "a single snap name is needed to specify the revision" +msgstr "a single snap name is needed to specify the revision" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "a single snap name must be specified when ignoring validation" + +msgid "active" +msgstr "active" + +msgid "bought" +msgstr "bought" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "broken" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "cannot buy snap: %v" + +msgid "cannot buy snap: invalid characters in name" +msgstr "cannot buy snap: invalid characters in name" + +msgid "cannot buy snap: it has already been bought" +msgstr "cannot buy snap: it has already been bought" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "cannot create %q: %v" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "cannot create assertions file: %v" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "cannot extract the snap-name from local file %q: %v" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "cannot find app %q in %q" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "cannot find hook %q in %q" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "cannot get data for %q: %v" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "cannot get full path for %q: %v" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "cannot get the current user: %s" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "cannot get the current user: %v" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "cannot mark boot successful: %s" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "cannot open the assertions database: %v" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "cannot read assertion input: %v" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "cannot read symlink: %v" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "cannot resolve snap app %q: %v" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "cannot sign assertion: %v" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "cannot update the 'current' symlink of %q: %v" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "cannot use %q key: %v" + +msgid "cannot use --hook and --command together" +msgstr "cannot use --hook and --command together" + +msgid "cannot use change ID and type together" +msgstr "cannot use change ID and type together" + +msgid "cannot use devmode and jailmode flags together" +msgstr "cannot use devmode and jailmode flags together" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "cannot validate owner of file %s" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "cannot write new Xauthority file at %s: %s" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "change finished in status %q with no error message" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" + +#, c-format +msgid "created user %q\n" +msgstr "created user %q\n" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "disabled" + +msgid "email:" +msgstr "e-mail:" + +msgid "enabled" +msgstr "enabled" + +#, c-format +msgid "error: %v\n" +msgstr "error: %v\n" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" +"error: the `` argument was not provided and couldn't be inferred" + +msgid "get which option?" +msgstr "get which option?" + +msgid "inactive" +msgstr "inactive" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" +"interface attributes can only be read during the execution of interface hooks" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" +"interface attributes can only be set during the execution of prepare hooks" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "internal error, please report: running %q failed: %v\n" + +msgid "internal error: cannot find attrs task" +msgstr "internal error: cannot find attrs task" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" +"internal error: cannot find plug or slot data in the appropriate task" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "internal error: cannot get %s from appropriate task" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "invalid attribute: %q (want key=value)" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "invalid configuration: %q (want key=value)" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "invalid header filter: %q (want key=value)" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "invalid parameter: %q (want key=value)" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "invalid value: %q (want snap:name or snap)" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "missing snap-confine: try updating your snapd package" + +msgid "need the application to run as argument" +msgstr "need the application to run as argument" + +msgid "no changes found" +msgstr "no changes found" + +#, c-format +msgid "no changes of type %q found" +msgstr "no changes of type %q found" + +msgid "no interfaces currently connected" +msgstr "no interfaces currently connected" + +msgid "no interfaces found" +msgstr "no interfaces found" + +msgid "no matching snaps installed" +msgstr "no matching snaps installed" + +msgid "no such interface" +msgstr "no such interface" + +msgid "no valid snaps given" +msgstr "no valid snaps given" + +msgid "not a valid snap" +msgstr "not a valid snap" + +msgid "please provide change ID or type with --last=" +msgstr "please provide change ID or type with --last=" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "private" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" + +#, c-format +msgid "set failed: %v" +msgstr "set failed: %v" + +msgid "set which option?" +msgstr "set which option?" + +msgid "show detailed information about a snap" +msgstr "show detailed information about a snap" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "snap %%q not found (at least at revision %q)" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "snap %%q not found (at least in channel %q)" + +#, c-format +msgid "snap %q has no updates available" +msgstr "snap %q has no available updates" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "snap %q is already installed, see \"snap refresh --help\"" + +#, c-format +msgid "snap %q is local" +msgstr "snap %q is local" + +#, c-format +msgid "snap %q not found" +msgstr "snap %q not found" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "snap is free" + +msgid "too many arguments for command" +msgstr "too many arguments for command" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "too many arguments for hook %q: %s" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "too many arguments: %s" + +msgid "unavailable" +msgstr "unavailable" + +#, c-format +msgid "unknown attribute %q" +msgstr "unknown attribute %q" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "unknown command %q, see \"snap --help\"" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "unknown plug or slot %q" + +#, c-format +msgid "unsupported shell %v" +msgstr "unsupported shell %v" + +#~ msgid "" +#~ "\n" +#~ "The change command displays a summary of tasks associated to an individual " +#~ "change." +#~ msgstr "" +#~ "\n" +#~ "The change command displays a summary of tasks associated to an individual " +#~ "change." + +#~ msgid "" +#~ "\n" +#~ "The find command queries the store for available packages.\n" +#~ msgstr "" +#~ "\n" +#~ "The find command queries the store for available packages.\n" + +#~ msgid "" +#~ "Show last change of given type (install, refresh, remove, try, auto-refresh " +#~ "etc.)" +#~ msgstr "" +#~ "Show last change of given type (install, refresh, remove, try, auto-refresh " +#~ "etc.)" diff -Nru snapd-2.28.5/po/es.po snapd-2.29.3/po/es.po --- snapd-2.28.5/po/es.po 2016-04-14 20:59:39.000000000 +0000 +++ snapd-2.29.3/po/es.po 2017-10-27 12:23:38.000000000 +0000 @@ -7,463 +7,1799 @@ msgstr "" "Project-Id-Version: snappy\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2015-10-15 15:53+0200\n" -"PO-Revision-Date: 2015-09-13 12:42+0000\n" -"Last-Translator: Adolfo Jayme \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-01-15 06:39+0000\n" +"Last-Translator: Paco Molinero \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2015-10-21 06:21+0000\n" -"X-Generator: Launchpad (build 17812)\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" -#. TRANSLATORS: the %s is a pkgname, the second a comma separated list of paths #, c-format -msgid "%s: %s\n" -msgstr "%s: %s\n" +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" -#. TRANSLATORS: the %s stand for "name", "version", "description" #, c-format -msgid "%s\t%s\t%s (forks not shown: %d)\t" -msgstr "%s\t%s\t%s (bifurcaciones no mostradas: %d)\t" +msgid "%q switched to the %q channel\n" +msgstr "" -#. TRANSLATORS: the first %s is a pkgname, the second %s is a path +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). #, c-format -msgid "'%s' is no longer allowed to access '%s'\n" -msgstr "«%s» ya no está autorizado para acceder a «%s»\n" +msgid "%s %s mounted from %s\n" +msgstr "%s %s montado desde %s\n" -#. TRANSLATORS: the first %s is a pkgname, the second %s is a path #, c-format -msgid "'%s' is now allowed to access '%s'\n" -msgstr "«%s» ahora tiene autorización para acceder a «%s»\n" +msgid "%s (delta)" +msgstr "" -#. TRANSLATORS: the first %s is a pkgname, the second %s is a path +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) #, c-format -msgid "'%s' previously allowed access to '%s'. Skipping\n" -msgstr "«%s» ya estaba autorizado para acceder a «%s». Omitido\n" +msgid "%s (see \"snap login --help\")" +msgstr "%s (vea «snap login --help»)" -#. TRANSLATORS: the %s is a pkgname +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) #, c-format -msgid "'%s:' is not allowed to access additional hardware\n" -msgstr "«%s»: no está autorizado para acceder a hardware adicional\n" +msgid "%s (try with sudo)" +msgstr "%s (intente con sudo)" -msgid "(deprecated) please use \"list\"" -msgstr "(obsoleto) use «list»" +#, c-format +msgid "%s already installed\n" +msgstr "%s ya instalado\n" -msgid "2fa code: " -msgstr "Código 2fa: " +#, c-format +msgid "%s disabled\n" +msgstr "%s desactivado\n" -msgid "" -"A concise summary of key attributes of the snappy system, such as the " -"release and channel.\n" -"\n" -"The verbose output includes the specific version information for the factory " -"image, the running image and the image that will be run on reboot, together " -"with a list of the available channels for this image.\n" -"\n" -"Providing a package name will display information about a specific installed " -"package.\n" -"\n" -"The verbose version of the info command for a package will also tell you the " -"available channels for that package, when it was installed for the first " -"time, disk space utilization, and in the case of frameworks, which apps are " -"able to use the framework." +#, c-format +msgid "%s enabled\n" +msgstr "%s activado\n" + +#, c-format +msgid "%s not installed\n" +msgstr "%s no instalado\n" + +#, c-format +msgid "%s removed\n" +msgstr "%s eliminado\n" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "%s revertido a %s\n" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "%s%s %s de «%s» instalado\n" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "%s%s %s desde «%s» actualizado\n" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "%s%s %s instalado\n" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "%s%s %s actualizado\n" + +msgid "--list does not take mode nor channel flags" +msgstr "--list no usa los indicadores modo ni canal" + +msgid "--time does not take mode nor channel flags" msgstr "" -msgid "Activate a package" +msgid "-r can only be used with --hook" +msgstr "-r solo se puede usar con --hook" + +msgid "" msgstr "" -msgid "" -"Activate a package that has previously been deactivated. If the package is " -"already activated, do nothing." +msgid "" +msgstr "" + +msgid "" msgstr "" -msgid "" -"Allows rollback of a snap to a previous installed version. Without any " -"arguments, the previous installed version is selected. It is also possible " -"to specify the version to rollback to as a additional argument.\n" +msgid "" msgstr "" -"Permite volver a una versión de snap instalada anteriormente. Sin " -"argumentos, se selecciona la versión anterior. Es posible especificar la " -"versión a la que volver como un argumento adicional.\n" -msgid "Assign a hardware device to a package" -msgstr "Asignar un dispositivo de hardware a un paquete" +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr ":" + +msgid "Abort a pending change" +msgstr "Abortar un cambio pendiente" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "Añadir una aserción al sistema" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "Alias para --dangerous (OBSOLETO)" + +msgid "All snaps up to date." +msgstr "Todos los snaps están actualizados." + +msgid "Alternative command to run" +msgstr "Orden alternativa a ejecutar" + +msgid "Always return document, even with single key" +msgstr "Devolver siempre un documento, incluso con clave única" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "Una dirección de correo de un usuario en login.ubuntu.com" -msgid "Assign hardware to a specific installed package" +msgid "Assertion file" +msgstr "Fichero de aserción" + +msgid "Assertion type name" +msgstr "Nombre del tipo de aserción" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" msgstr "" -msgid "Builds a snap package" -msgstr "Construye un paquete snap" +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" -#. TRANSLATORS: the first %q is the file that can not be read and %v is the error message +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names #, c-format -msgid "Can't read hook file %q: %v" -msgstr "No se puede leer el archivo de «hook» %q: %v" +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "Código incorrecto. Inténtelo de nuevo: " + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "Cambiar ID" + +msgid "Changes configuration options" +msgstr "Opciones de configuración de cambios" msgid "" -"Configures a package. The configuration is a YAML file, provided in the " -"specified file which can be \"-\" for stdin. Output of the command is the " -"current configuration, so running this command with no input file provides a " -"snapshot of the app's current config." +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "Confirmar contraseña: " + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "Conectar %s:%s a %s:%s" + +msgid "Connects a plug to a slot" +msgstr "Conectar un enchufe al zócalo" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" msgstr "" -"Configura un paquete. La configuración es un archivo YAML, suministrado en " -"el archivo especificado que puede ser «-» para stdin. La salida de la orden " -"es la configuración actual, de forma que ejecutar esta orden sin el archivo " -"brinda una copia de la configuración actual de la aplicación." -msgid "Creates a snap package and if available, runs the review scripts." +msgid "Constrain listing to those matching header=value" msgstr "" -"Crea un paquete snap y, si están disponibles, ejecuta los «scripts» de " -"revisión." -msgid "Deactivate a package" +#, c-format +msgid "Copy snap %q data" msgstr "" msgid "" -"Deactivate a package. If the package is already deactivated, do nothing." +"Create a cryptographic key pair that can be used for signing assertions." msgstr "" -msgid "Display a summary of key attributes of the snappy system." -msgstr "Muestra un resumen de los atributos clave de el sistema snappy." +msgid "Create cryptographic key pair" +msgstr "Crear un par de claves de cifrado" + +msgid "Create snap build assertion" +msgstr "" -msgid "Do not clean up old versions of the package." +msgid "Create snap-build assertion for the provided snap file." msgstr "" -msgid "Ensures system is running with latest parts" +msgid "Creates a local system user" +msgstr "Crear un usuario local de sistema" + +msgid "Delete cryptographic key pair" +msgstr "Eliminar par de claves de cifrado" + +msgid "Delete the local cryptographic key pair with the given name." msgstr "" -"Se asegura que el sistema está ejecutando con las partes más recientes" -msgid "First boot has already run" -msgstr "El primer arranque ya se ejecutó" +#, c-format +msgid "Disable %q snap" +msgstr "Desactivar %q snap" -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "Generated '%s' snap\n" -msgstr "Snap «%s» generado\n" +msgid "Disable aliases for snap %q" +msgstr "Desactivar alias para el snap %q" -msgid "Include information about packages from the snappy store" +#, c-format +msgid "Disable all aliases for snap %q" msgstr "" -msgid "Install a snap package" -msgstr "Instalar un paquete snap" +msgid "Disables a snap in the system" +msgstr "Desactivar un snap en el sistema" -msgid "Install snaps even if the signature can not be verified." +#, c-format +msgid "Discard interface connections for snap %q (%s)" msgstr "" -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "Installing %s\n" -msgstr "Instalando %s\n" +msgid "Disconnect %s:%s from %s:%s" +msgstr "Desconectar %s:%s de %s:%s" + +msgid "Disconnects a plug from a slot" +msgstr "Desconectar un enchufe del zócalo" -msgid "List active components installed on a snappy system" -msgstr "Listar los componentes activos instalados en un sistema snappy" +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" -msgid "List assigned hardware device for a package" -msgstr "Listar dispositivos de hardware asignados para un paquete" +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "Descargar snap %q%s del canal %q" -msgid "List assigned hardware for a specific installed package" +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" msgstr "" -msgid "Log into the store" -msgstr "Iniciar sesión en la tienda" +msgid "Downloads the given snap" +msgstr "Descargar el snap dado" -msgid "Login successful" -msgstr "Inicio de sesión correcto" +msgid "Email address: " +msgstr "Dirección de correo electrónico: " + +#, c-format +msgid "Enable %q snap" +msgstr "Activar el snap %q" + +msgid "Enables a snap in the system" +msgstr "Activa un snap en el sistema" + +msgid "Entering classic dimension" +msgstr "Introducir la dimensión clásica" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" -msgid "Name\tDate\tVersion\t" -msgstr "Nombre\tFecha\tVersión\t" +msgid "Export cryptographic public key" +msgstr "Exportar clave pública de cifrado" -msgid "Name\tDate\tVersion\tDeveloper\t" -msgstr "Nombre\tFecha\tVersión\tDesarrollador\t" +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" -msgid "Name\tVersion\tSummary\t" -msgstr "Nombre\tVersión\tResumen\t" +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "No snap: '%s' found" -msgstr "Nigún snap: «%s» encontrado" +msgid "Fetching snap %q\n" +msgstr "Traer snap %q\n" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" -msgid "Password: " -msgstr "Contraseña: " +msgid "Finds packages to install" +msgstr "Buscar paquetes para instalar" -msgid "Provide information about a specific installed package" +msgid "Force adding the user, even if the device is already managed" msgstr "" +msgid "Force import on classic systems" +msgstr "Forzar importación en sistemas clásicos" + msgid "" -"Provides a list of all active components installed on a snappy system.\n" -"\n" -"If requested, the command will find out if there are updates for any of the " -"components and indicate that by appending a * to the date. This will be " -"slower as it requires a round trip to the app store on the network.\n" -"\n" -"The developer information refers to non-mainline versions of a package (much " -"like PPAs in deb-based Ubuntu). If the package is the primary version of " -"that package in Ubuntu then the developer info is not shown. This allows one " -"to identify packages which have custom, non-standard versions installed. As " -"a special case, the \"sideload\" developer refers to packages installed " -"manually on the system.\n" -"\n" -"When a verbose listing is requested, information about the channel used is " -"displayed; which is one of alpha, beta, rc or stable, and all fields are " -"fully expanded too. In some cases, older (inactive) versions of snappy " -"packages will be installed, these will be shown in the verbose output and " -"the active version indicated with a * appended to the name of the component." +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "Generar clave de dispositivo" + +msgid "Generate the manpage" +msgstr "Generar la página de manual" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "Garantizar acceso de sudo al usuario creado" + +msgid "Help" +msgstr "Ayuda" + +msgid "Hook to run" msgstr "" -msgid "Provides more detailed information" +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" msgstr "" -msgid "Purge an installed package." +msgid "Identifier of the signer" +msgstr "Identificador del firmante" + +msgid "Identifier of the snap package associated with the build" +msgstr "Identificador del paquete de snap asociado con la construcción" + +msgid "Ignore validation by other snaps blocking the refresh" msgstr "" -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "Purging %s\n" -msgstr "Purgando %s\n" +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" -msgid "Query and modify snappy services" +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" msgstr "" -msgid "Query and modify snappy services of locally-installed packages" +msgid "Include unused interfaces" msgstr "" -msgid "Query the store for available packages" -msgstr "Consultar la tienda por paquetes disponibles" +msgid "Initialize device" +msgstr "Inicializar dispositivo" -msgid "Reboot if necessary to be on the latest running system." +msgid "Inspects devices for actionable information" msgstr "" -#. TRANSLATORS: the first %s is a pkgname the second a version #, c-format -msgid "Reboot to use %s version %s." -msgstr "Reinicie para usar %s versión %s." +msgid "Install %q snap" +msgstr "Instalar snap %q" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "Instalar snap %q desde el canal %q" -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "Reboot to use the new %s." -msgstr "Reinicie para usar el nuevo %s." +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "Instalar desde el canal beta" + +msgid "Install from the candidate channel" +msgstr "Instalar desde el canal candidato" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "Instalar desde el canal estable" -#. TRANSLATORS: the %s shows a comma separated list -#. of package names #, c-format -msgid "Rebooting to satisfy updates for %s\n" -msgstr "Reiniciando para satisfacer las actualizaciones para %s\n" +msgid "Install snap %q" +msgstr "Instalar snap %q" -msgid "Remove a snapp part" -msgstr "Eliminar una parte de snap" +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "Instalar snaps %s" -msgid "Remove all the data from the listed packages" -msgstr "Eliminar todos los datos de los paquetes listados" +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" msgid "" -"Remove all the data from the listed packages. Normally this is used for " -"packages that have been removed and attempting to purge data for an " -"installed package will result in an error. The --installed option overrides " -"that and enables the administrator to purge all data for an installed " -"package (effectively resetting the package completely)." +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" msgstr "" -msgid "Remove hardware from a specific installed package" +msgid "Install the given snap without enabling its automatic aliases" msgstr "" -#. TRANSLATORS: the %s is a pkgname -#, c-format -msgid "Removing %s\n" -msgstr "Eliminando %s\n" +msgid "Installs a snap to the system" +msgstr "Instalar un snap en el sistema" + +msgid "Key of interest within the configuration" +msgstr "" -msgid "Rollback to a previous version of a package" -msgstr "Volver a una versión anterior de un paquete" +msgid "List a change's tasks" +msgstr "Listar tareas de cambio" -msgid "Search for packages to install" -msgstr "Buscar paquetes para instalar" +msgid "List cryptographic keys" +msgstr "Listar claves de cifrado" -msgid "Set configuration for a specific installed package" +msgid "List cryptographic keys that can be used for signing assertions." msgstr "" -msgid "Set configuration for an installed package." -msgstr "Establecer la configuración de un paquete instalado" +msgid "List installed snaps" +msgstr "Listar snaps instalados" -msgid "Set properties of system or package" -msgstr "Establecer las propiedades de un sistema o paquete" +msgid "List system changes" +msgstr "Listar cambios de sistema" -msgid "" -"Set properties of system or package\n" -"\n" -"Supported properties are:\n" -" active=VERSION\n" -"\n" -"Example:\n" -" set hello-world active=1.0\n" +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "Listar interfaces en el sistema" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "Salir de la tienda" + +msgid "Login successful" +msgstr "Inicio de sesión correcto" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "Hacer el snap %q (%s) disponible en el sistema" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "Hacer el snap %q (%s) no disponible en el sistema" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "Hacer el snap %q no disponible en el sistema" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" msgstr "" -#. TRANSLATORS: the first %s is a pkgname, the second %s is the new version #, c-format -msgid "Setting %s to version %s\n" -msgstr "Estableciendo %s a la versión %s\n" +msgid "Mount snap %q%s" +msgstr "Montar snap %q%s" -msgid "Show all available forks of a package" +msgid "Name of key to create; defaults to 'default'" msgstr "" -msgid "Show available updates (requires network)" +msgid "Name of key to delete" +msgstr "Nombre de la clave a eliminar" + +msgid "Name of key to export" +msgstr "Nombre de la clave a exportar" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" msgstr "" -msgid "Show channel information and expand all fields" +msgid "Name\tSHA3-384" +msgstr "Nombre \tSHA3-384" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" msgstr "" -msgid "Snap\tService\tState" +msgid "Name\tVersion\tRev\tDeveloper\tNotes" msgstr "" -msgid "Specify an alternate output directory for the resulting package" +msgid "No snaps are installed yet. Try \"snap install hello-world\"." msgstr "" -msgid "The Package to install (name or path)" +msgid "No snaps to auto-refresh found" msgstr "" +msgid "Output results in JSON format" +msgstr "Resultados de salida en formato JSON" + +msgid "Passphrase: " +msgstr "Frase de paso: " + +#, c-format +msgid "Password of %q: " +msgstr "Contraseña para %q:amp> " + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format msgid "" -"The \"versions\" command is no longer available.\n" -"\n" -"Please use the \"list\" command instead to see what is installed.\n" -"The \"list -u\" (or \"list --updates\") will show you the available updates\n" -"and \"list -v\" (or \"list --verbose\") will show all installed versions.\n" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" msgstr "" -msgid "The configuration for the given file" +msgid "Prefer aliases from a snap and disable conflicts" msgstr "" -msgid "The configuration for the given install" +#, c-format +msgid "Prefer aliases of snap %q" msgstr "" -msgid "The hardware device path (e.g. /dev/ttyUSB0)" +msgid "Prepare a snappy image" +msgstr "Preparar una imagen de snappy" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "Preparar snap %q (%s)" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "Preparar snap %q%s" + +msgid "Print the version and exit" +msgstr "Imprimir la versión y salir" + +msgid "Prints configuration options" +msgstr "Imprimir las opciones de configuración" + +msgid "Prints the confinement mode the system operates in" msgstr "" -msgid "The package to rollback " +msgid "Prints the email the user is logged in with." msgstr "" -msgid "The version to rollback to" +msgid "Prints whether system is managed" msgstr "" -msgid "" -"This command adds access to a specific hardware device (e.g. /dev/ttyUSB0) " -"for an installed package." +#, c-format +msgid "Prune automatic aliases for snap %q" msgstr "" -"Esta orden agrega el acceso a un dispositivo hardware específico (p.ej. " -"/dev/ttyUSB0) para un paquete instalado." -msgid "This command is no longer available, please use the \"list\" command" -msgstr "Esta orden ya no está disponible. Utilice «list» en su lugar" +msgid "Put snap in classic mode and disable security confinement" +msgstr "" -msgid "This command list what hardware an installed package can access" +msgid "Put snap in development mode and disable security confinement" msgstr "" -"Esta orden lista el hardware al que tiene acceso un paquete instalado" -msgid "This command logs the given username into the store" -msgstr "Esta orden inicia una sesión del usuario especificado en la tienda" +msgid "Put snap in enforced confinement mode" +msgstr "" -msgid "" -"This command removes access of a specific hardware device (e.g. " -"/dev/ttyUSB0) for an installed package." +msgid "Query the status of services" msgstr "" -"Esta orden elimina el acceso a un dispositivo de hardware específico (p.ej. " -"/dev/ttyUSB0) para un paquete instalado." -msgid "Unassign a hardware device to a package" -msgstr "Desasignar un dispositivo de hardware a un paquete" +#, c-format +msgid "Refresh %q snap" +msgstr "Actualizar snap %q" -msgid "Update all installed parts" -msgstr "Actualizar todas las partes intaladas" +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" -msgid "Use --show-all to see all available forks." -msgstr "Use --show-all para ver todas las bifurcaciones disponibles." +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" -msgid "Username for the login" +msgid "Refresh all snaps: no updates" msgstr "" -#. TRANSLATORS: the %s represents a list of installed appnames -#. (e.g. "apps: foo, bar, baz") #, c-format -msgid "apps: %s\n" -msgstr "aplicaciones: %s\n" +msgid "Refresh snap %q" +msgstr "Actualizar snap %q" -#. TRANSLATORS: the %s an architecture string +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names #, c-format -msgid "architecture: %s\n" -msgstr "arquitectura: %s\n" +msgid "Refresh snaps %s" +msgstr "Actualizar snaps %s" -#. TRANSLATORS: the %s is a size +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names #, c-format -msgid "binary-size: %v\n" -msgstr "tamaño del binario: %v\n" +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "Actualizar a la versión dada" + +msgid "Refreshes a snap in the system" +msgstr "Actualizar un snap en el sistema" -#. TRANSLATORS: the %s is a channel name #, c-format -msgid "channel: %s\n" -msgstr "canal: %s\n" +msgid "Remove %q snap" +msgstr "Eliminar snap %q" -#. TRANSLATORS: the %s is a size #, c-format -msgid "data-size: %s\n" -msgstr "tamaño de los datos: %s\n" +msgid "Remove aliases for snap %q" +msgstr "Eliminar alias para el snap %q" -#. TRANSLATORS: the %s is a comma separated list of framework names #, c-format -msgid "frameworks: %s\n" -msgstr "marcos: %s\n" +msgid "Remove data for snap %q (%s)" +msgstr "Eliminar datos para el snap %q (%s)" -#. TRANSLATORS: the %s is a date #, c-format -msgid "installed: %s\n" -msgstr "instalado: %s\n" +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" -msgid "package name is required" -msgstr "se requiere el nombre del paquete" +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" -msgid "produces manpage" +#, c-format +msgid "Remove security profiles of snap %q" msgstr "" -#. TRANSLATORS: the %s release string #, c-format -msgid "release: %s\n" -msgstr "publicación: %s\n" +msgid "Remove snap %q" +msgstr "Eliminar snap %q" -msgid "" -"snappy autopilot triggered a reboot to boot into an up to date system -- " -"temprorarily disable the reboot by running 'sudo shutdown -c'" +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "Eliminar snaps %s" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "Eliminar un snap del sistema" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" msgstr "" -"snappy autopilot provocó un reinicio para arrancar en un sistema actualizado " -"-- desactive temporalmente el reinicio ejecutando «sudo shutdown -c»" -#. TRANSLATORS: the first %s is the package name, the second is the service name; the %v is the error #, c-format -msgid "unable to disable %s's service %s: %v" +msgid "Run configure hook of %q snap" msgstr "" -#. TRANSLATORS: the first %s is the package name, the second is the service name; the %v is the error #, c-format -msgid "unable to enable %s's service %s: %v" +msgid "Run configure hook of %q snap if present" msgstr "" #, c-format -msgid "unable to get logs: %v" +msgid "Run hook %s of snap %q" msgstr "" -#. TRANSLATORS: the first %s is the package name, the second is the service name; the %v is the error #, c-format -msgid "unable to start %s's service %s: %v" +msgid "Run install hook of %q snap if present" msgstr "" -#. TRANSLATORS: the first %s is the package name, the second is the service name; the %v is the error #, c-format -msgid "unable to stop %s's service %s: %v" +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" msgstr "" -#. TRANSLATORS: the %s is a date #, c-format -msgid "updated: %s\n" -msgstr "actualizado: %s\n" +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "Buscar snaps privados" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "Mostrar todas las revisiones" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "Mostrar los detalles de la versión" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "Nombre de snap" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "Iniciar los servicios (%s) del snap %q" + +#, c-format +msgid "Start snap %q%s services" +msgstr "Iniciar los servicios %q%s del snap" + +msgid "Start snap services" +msgstr "Iniciar servicios snap" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "Parar los servicios (%s) del snap %q" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "Parar los servicios de snap" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "Probar un snap en el sistema" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "El email con el que acceder a login.ubuntu.com" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "DIrectorio de salida" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "El snap a configurar (p.ej.: hello-world)" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "Herramienta para actuar con snaps" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "Código de dos factores: " + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "Esperando al servidor para reiniciar" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "Incorrecto de nuevo. Una vez más: " + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "Sí, sí lo hace." + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" +"\n" +"La orden install instala el snap nombrado en el sistema.\n" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "roto" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "no se puede crear %q: %v" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "no se puede encontar la app %q en %q" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "no se puede usar la clave %q : %v" + +msgid "cannot use --hook and --command together" +msgstr "no se pueden usar --hook y --command juntos" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "desactivado" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "error: %v\n" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "no se han encontrado cambios" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "no mse han encontrado interfaces" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "privado" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "snap es gratuito" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "demasiados argumentos: %s" + +msgid "unavailable" +msgstr "no disponible" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "órden %q desconocida, vea «snap --help»" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" -#. TRANSLATORS: the %s is a version string #, c-format -msgid "version: %s\n" -msgstr "versión: %s\n" +msgid "unsupported shell %v" +msgstr "intérprete no permitido %w" diff -Nru snapd-2.28.5/po/fi.po snapd-2.29.3/po/fi.po --- snapd-2.28.5/po/fi.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/fi.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Finnish translation for snapd +# Copyright (c) 2017 Rosetta Contributors and Canonical Ltd 2017 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-09-19 07:05+0000\n" +"Last-Translator: Jiri Grönroos \n" +"Language-Team: Finnish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "%s (delta)" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/fr.po snapd-2.29.3/po/fr.po --- snapd-2.28.5/po/fr.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/fr.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,2237 @@ +# French translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-08-17 17:23+0000\n" +"Last-Translator: Anne017 \n" +"Language-Team: French \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" +"%q ne contient aucun paquet Snap décompressé.\n" +"\n" +"Essayez « snapcraft prime » dans votre répertoire de projet, puis à nouveau " +"« snap try »." + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "%s %s monté depuis %s\n" + +#, c-format +msgid "%s (delta)" +msgstr "%s (delta)" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "%s (voir « snap login --help »)" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "%s (essayez avec sudo)" + +#, c-format +msgid "%s already installed\n" +msgstr "%s est déjà installé\n" + +#, c-format +msgid "%s disabled\n" +msgstr "%s désactivé\n" + +#, c-format +msgid "%s enabled\n" +msgstr "%s activé\n" + +#, c-format +msgid "%s not installed\n" +msgstr "%s n'est pas installé\n" + +#, c-format +msgid "%s removed\n" +msgstr "%s supprimé\n" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "%s revenu à %s\n" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "%s%s %s de « %s » installé\n" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "%s%s %s de « %s » actualisé\n" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "%s%s %s installé\n" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "%s%s %s actualisé\n" + +msgid "--list does not take mode nor channel flags" +msgstr "" +"--list n'accepte ni les indicateurs de mode ni les indicateurs de canal" + +msgid "--time does not take mode nor channel flags" +msgstr "--time n'accepte pas les options mode et/ou canal" + +msgid "-r can only be used with --hook" +msgstr "-r ne peut être utilisé qu'avec --hook" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr ":" + +msgid ":" +msgstr ":" + +msgid ":" +msgstr ":" + +msgid "Abort a pending change" +msgstr "Annuler un changement en attente" + +msgid "Added" +msgstr "Ajouté" + +msgid "Adds an assertion to the system" +msgstr "Ajoute une assertion au système" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "Alias pour --dangerous (OBSOLÈTE)" + +msgid "All snaps up to date." +msgstr "Tous les paquets Snaps sont à jour." + +msgid "Alternative command to run" +msgstr "Commande alternative à « run »" + +msgid "Always return document, even with single key" +msgstr "Toujours retourner un document, même avec une clé unique" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "Un courriel d'un utilisateur sur login.ubuntu.com" + +msgid "Assertion file" +msgstr "Fichier d'assertion" + +msgid "Assertion type name" +msgstr "Nom du type d'assertion" + +msgid "Authenticates on snapd and the store" +msgstr "Authentifie sur Snapd et sur la logithèque" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "Actualiser automatiquement %d paquets Snap" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "Actualiser automatiquement le paquet Snap %q" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "Actualiser automatiquement les paquets Snap %s" + +msgid "Bad code. Try again: " +msgstr "Code faux. Réessayez : " + +msgid "Buys a snap" +msgstr "Achète un paquet Snap" + +msgid "Change ID" +msgstr "Changer l'identifiant" + +msgid "Changes configuration options" +msgstr "Modifie les options de configuration" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" +"Dimension classique désactivée sur ce système.\n" +"Utilisez « sudo snap install --devmode classic && sudo classic.create » pour " +"l'activer." + +msgid "Command\tAlias\tNotes" +msgstr "Commande\tAlias\tNotes" + +msgid "Configuration value (key=value)" +msgstr "Valeur de configuration (clé=valeur)" + +msgid "Confirm passphrase: " +msgstr "Confirmez la phrase de passe : " + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "Connecter %s:%s à %s:%s" + +msgid "Connects a plug to a slot" +msgstr "Connecte un connecteur à une prise" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "Limite la liste à un paquet Snap spécifique ou snap:nom" + +msgid "Constrain listing to specific interfaces" +msgstr "Limite la liste à des interfaces spécifiques" + +msgid "Constrain listing to those matching header=value" +msgstr "Limite la liste aux éléments correspondant à en-tête=valeur" + +#, c-format +msgid "Copy snap %q data" +msgstr "Copier les données du paquet Snap %q" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" +"Créer une paire de clés chiffrées qui peut être utilisée pour la signature " +"des assertions." + +msgid "Create cryptographic key pair" +msgstr "Créer une paire de clés chiffrées" + +msgid "Create snap build assertion" +msgstr "Créer une assertion de construction du paquet Snap" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" +"Créer une assertion de construction du paquet Snap pour le fichier Snap " +"fourni." + +msgid "Creates a local system user" +msgstr "Crée un utilisateur local du système" + +msgid "Delete cryptographic key pair" +msgstr "Supprimer une paire de clés chiffrées" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" +"Supprimer la paire de clés locales chiffrées possédant le nom indiqué." + +#, c-format +msgid "Disable %q snap" +msgstr "Paquet Snap %q désactivé" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "Désactiver les alias pour le paquet Snap %q" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "Désactive tous les alias pour le paquet Snap %q" + +msgid "Disables a snap in the system" +msgstr "Désactive un paquet Snap dans le système" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "Effacer les connexions entre interfaces pour le paquet Snap %q (%s)" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "Déconnecter %s:%s de %s:%s" + +msgid "Disconnects a plug from a slot" +msgstr "Déconnecte un connecteur d'une prise" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" +"Ne pas attendre la fin de l'opération, mais afficher simplement " +"l'identifiant de modification." + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "Télécharger un paquet Snap %q%s à partir du canal %q" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" +"Télécharger la révision donnée d'un paquet Snap, vers laquelle vous devez " +"avoir des accès développeur" + +msgid "Downloads the given snap" +msgstr "Télécharge le paquet Snap en question" + +msgid "Email address: " +msgstr "Adresse électronique : " + +#, c-format +msgid "Enable %q snap" +msgstr "Activer le paquet Snap %q" + +msgid "Enables a snap in the system" +msgstr "Active un paquet Snap dans le système" + +msgid "Entering classic dimension" +msgstr "Entrée dans la dimension classique" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" +"Exporter un corps d'assertion de clé publique qui puisse être importé par " +"d'autres systèmes." + +msgid "Export cryptographic public key" +msgstr "Exporter une clé de chiffrement publique" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "Récupérer et vérifier les assertions pour le paquet Snap %q%s" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "Récupération des assertions pour %q\n" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "Récupération du paquet Snap %q\n" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" +"Le nom du fichier du paquet Snap pour lequel vous voulez définir une version" + +msgid "Finds packages to install" +msgstr "Trouve les paquets à installer" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" +"Forcer l'ajout de l'utilisateur, même si l'appareil est déjà administré" + +msgid "Force import on classic systems" +msgstr "Forcer l'importation dans les systèmes classiques" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" +"Formater le matériel de clef publique comme une demande de clef de compte " +"pour cet identifiant de compte" + +msgid "Generate device key" +msgstr "Générer la clé du périphérique" + +msgid "Generate the manpage" +msgstr "Générer les page de manuel" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" +"Le grade indique la qualité de version du paquet Snap (« stable » par défaut)" + +msgid "Grant sudo access to the created user" +msgstr "Accorder les accès administrateur à l'utilisateur créé" + +msgid "Help" +msgstr "Aide" + +msgid "Hook to run" +msgstr "Point d'accroche à exécuter" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "Identifiant\tÉtat\tDescendance\tPrêt\tRésumé\n" + +msgid "Identifier of the signer" +msgstr "Identifiant du signataire" + +msgid "Identifier of the snap package associated with the build" +msgstr "Identifiant du paquet Snap associé à la version" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" +"Ignorer la validation par d'autres paquets Snap bloquant l'actualisation" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" +"Pour acheter %q, vous devez accepter la dernière version des conditions " +"générales. Pour cela, allez sur https://my.ubuntu.com/payment/edit.\n" +"\n" +"Revenez ensuite ici et exécutez à nouveau « snap buy %s »." + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" +"Inclure une liste détaillée des notes d'un paquet Snap (résume les notes " +"dans le cas contraire)" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "Initialiser l'appareil" + +msgid "Inspects devices for actionable information" +msgstr "Cherche les informations actionnables sur les appareils" + +#, c-format +msgid "Install %q snap" +msgstr "Installer le paquet Snap %q" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "Installer le paquet Snap %q provenant du canal %q" + +#, c-format +msgid "Install %q snap from file" +msgstr "Installer le paquet Snap %q à partir du fichier" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "Installer le paquet Snap %q à partir du fichier %q" + +msgid "Install from the beta channel" +msgstr "Installer à partir du canal bêta" + +msgid "Install from the candidate channel" +msgstr "Installer à partir du canal candidat" + +msgid "Install from the edge channel" +msgstr "Installer à partir du canal avancé" + +msgid "Install from the stable channel" +msgstr "Installer à partir du canal stable" + +#, c-format +msgid "Install snap %q" +msgstr "Installer le paquet Snap %q" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "Installer les paquets Snaps %s" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" +"Installer la révision donnée d'un paquet Snap, auquel vous devez avoir les " +"accès développeur" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" +"Installer le paquet Snap donné même s'il n'y a pas de signatures reconnues " +"préalablement pour celui-ci, ce qui signifie qu'il n'a pas été vérifié et " +"pourrait être dangereux (--devmode implique cela)" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "Installe un paquet Snap sur le système" + +msgid "Key of interest within the configuration" +msgstr "Clef d'intérêt dans le cadre de la configuration" + +msgid "List a change's tasks" +msgstr "Lister les tâches d'une modification" + +msgid "List cryptographic keys" +msgstr "Lister les clés de chiffrement" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" +"Lister les clés de chiffrement qui peuvent être utilisées pour la signature " +"des assertions." + +msgid "List installed snaps" +msgstr "Lister les paquets Snap installés" + +msgid "List system changes" +msgstr "Lister les changements du système" + +msgid "Lists aliases in the system" +msgstr "Afficher la liste des alias dans le système" + +msgid "Lists interfaces in the system" +msgstr "Liste les interfaces dans le système" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "Se déconnecter de la logithèque" + +msgid "Login successful" +msgstr "Connexion réussie" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "Rendre indisponible la révision actuelle du paquet Snap %q" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "Rendre disponible le paquet Snap %q (%s) pour le système" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "Rendre indisponible le paquet Snap %q (%s) pour le système" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "Rendre indisponible le paquet Snap %q pour le système" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "Rendre disponible le paquet Snap %q%s pour le système" + +msgid "Mark system seeded" +msgstr "Marquer le système comme préparé" + +#, c-format +msgid "Mount snap %q%s" +msgstr "Monter le paquet Snap %q%s" + +msgid "Name of key to create; defaults to 'default'" +msgstr "Nom de la clé à créer ; « default » par défaut" + +msgid "Name of key to delete" +msgstr "Nom de la clé à supprimer" + +msgid "Name of key to export" +msgstr "Nom de la clé à exporter" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "Nom de la clé GnuPG à utiliser (nom de clé « default » par défaut)" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "Nom de la clé à utiliser, utilisation de la clé par défaut autrement" + +msgid "Name\tSHA3-384" +msgstr "Nom\tSHA3-384" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "Nom\tVersion\tDéveloppeur\tNotes\tRésumé" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "Nom\tVersion\tRévision\tDéveloppeur\tNotes" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" +"Aucun paquet Snap n'est encore installé. Essayez « snap install hello-" +"world »." + +msgid "No snaps to auto-refresh found" +msgstr "Aucun paquet Snap à actualiser n'a été trouvé" + +msgid "Output results in JSON format" +msgstr "Résultats de sortie en format JSON" + +msgid "Passphrase: " +msgstr "Phrase de passe : " + +#, c-format +msgid "Password of %q: " +msgstr "Mot de passe de %q : amp> " + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" +"Veuillez saisir à nouveau votre mot de passe Ubuntu One pour \n" +"acheter %q de %q pour %s. Appuyez sur Ctrl-C pour annuler." + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "Préfère les alias pour le paquet Snap %q" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "Préfère les alias d'un paquet Snap et désactive les conflits" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "Préfère les alias du paquet Snap %q" + +msgid "Prepare a snappy image" +msgstr "Préparer une image Snappy" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "Préparer le paquet Snap %q (%s)" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "Préparer le paquet Snap %q%s" + +msgid "Print the version and exit" +msgstr "Imprimer la version et quitter" + +msgid "Prints configuration options" +msgstr "Affiche les options de configuration" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" +"Affiche l'adresse électronique avec laquelle l'utilisateur est authentifié." + +msgid "Prints whether system is managed" +msgstr "Indique si le système est géré" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "Enlever les alias automatiques pour le paquet Snap %q" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" +"Placer le paquet Snap en mode classique et désactiver le confinement de " +"sécurité" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" +"Placer le paquet Snap en mode développement et désactiver le confinement de " +"sécurité" + +msgid "Put snap in enforced confinement mode" +msgstr "Placer le paquet Snap en mode de confinement forcé" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "Actualiser le paquet Snap %q" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "Actualiser le paquet Snap %q du canal %q" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "Actualise les alias pour le paquet Snap %q" + +msgid "Refresh all snaps: no updates" +msgstr "Actualiser tous les paquets Snap :pas de mise à jour" + +#, c-format +msgid "Refresh snap %q" +msgstr "Actualiser le paquet Snap %q" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "Actualiser les paquets Snap %s" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "Actualiser automatiquement les paquets Snap %s : pas de mise à jour" + +msgid "Refresh to the given revision" +msgstr "Actualiser la révision donnée" + +msgid "Refreshes a snap in the system" +msgstr "Actualise un paquet Snap du système" + +#, c-format +msgid "Remove %q snap" +msgstr "Supprimer le paquet Snap %q" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "Supprimer les alias pour le paquet Snap %q" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "Supprimer les données du paquet Snap %q (%s)" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "Supprime l'alias manuel %q pour le paquet Snap %q" + +msgid "Remove only the given revision" +msgstr "Supprimer uniquement la révision donnée" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "Supprimer le profil de sécurité du paquet Snap %q (%s)" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "Supprimer le profil de sécurité du paquet Snap %q" + +#, c-format +msgid "Remove snap %q" +msgstr "Supprimer le paquet Snap %q" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "Supprimer le paquet Snap %q (%s) du système" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "Supprimer les paquets Snap %s" + +msgid "Removed" +msgstr "Supprimé" + +msgid "Removes a snap from the system" +msgstr "Supprime un paquet Snap du système" + +msgid "Request device serial" +msgstr "Demander le numéro de série de l'appareil" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "Restreindre la recherche à une section donnée" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "Rétablir le paquet Snap %q" + +msgid "Reverts the given snap to the previous state" +msgstr "Rétablit le paquet Snap donné à son précédent état" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "Exécuter un shell plutôt que la commande (utile pour le débogage)" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "Exécuter la configuration du point d'accroche du paquet Snap %q" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" +"Exécuter la configuration du point d'accroche du paquet Snap %q s'il est " +"présent" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "Exécuter le point d'accroche %s du paquet Snap %q" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "Exécuter le point d'accroche prepare-device" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "Exécuter la commande Snap donnée" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" +"Exécuter la commande Snap donnée avec le confinement et l'environnement " +"corrects" + +msgid "Runs debug commands" +msgstr "Exécute les commandes de débogage" + +msgid "Search private snaps" +msgstr "Rechercher des paquets Snap privés" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "Définir les alias automatiques pour le paquet Snap %q" + +msgid "Sets up a manual alias" +msgstr "Définit un alias manuel" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "Définit l'alias %q => %q pour le paquet Snap %q" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "Définit l'alias manuel %q => %q pour le paquet Snap %q" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "Configurer les profils de sécurité du paquet Snap %q (%s)" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "Configurer les alias du paquet Snap %q" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "Définir les profils de sécurité du paquet Snap %q%s" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "Configurer les profils de sécurité du paquet Snap %q%s (phase 2)" + +msgid "Show all revisions" +msgstr "Afficher toutes les révisions" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" +"Affiche les informations de rafraîchissement automatique sans actualiser" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" +"Affiche les paquets Snap disponibles pour un rafraîchissement sans effectuer " +"d'actualisation" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "Affiche les assertions connues du type fourni" + +msgid "Shows version details" +msgstr "Afficher les détails de la version" + +msgid "Sign an assertion" +msgstr "Signer une assertion" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" +"Signer une assertion avec la clef spécifiée, en utilisant comme entrée pour " +"les en-têtes un modèle JSON fourni via stdin. Le corps de l'assertion peut " +"être spécifié via un pseudo-en-tête « body ».\n" + +msgid "Slot\tPlug" +msgstr "Prise\tConnecteur" + +msgid "Snap name" +msgstr "Nom du paquet Snap" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" +"Désolé, votre mode de paiement est refusé par l'émetteur. Consultez vos\n" +"détails de paiement sur https://my.ubuntu.com/payment/edit et réessayez." + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "Démarrer les services du paquet Snap %q (%s)" + +#, c-format +msgid "Start snap %q%s services" +msgstr "Démarrer les services du paquet Snap %q%s" + +msgid "Start snap services" +msgstr "Démarrer les services du paquet Snap" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "État\tDescendance\tPrêt\tRésumé\n" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "Arrêter les services du paquet Snap %q (%s)" + +#, c-format +msgid "Stop snap %q services" +msgstr "Arrêter les services du paquet Snap %q" + +msgid "Stop snap services" +msgstr "Arrêter les services du paquet Snap" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "Frappe exacte avec chaînes vides et entre guillemets" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "Passer le paquet Snap %q de %s à %s" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "Monter le périphérique temporairement avant inspection" + +msgid "Tests a snap in the system" +msgstr "Teste un paquet Snap dans le système" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" +"Merci d'avoir acheté %q. Vous pouvez à présent l'installer sur vos " +"appareils\n" +"via un « snap install %s »." + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" +"La commande « get » affiche les paramètres de configuration et de " +"l'interface de connexion." + +msgid "The login.ubuntu.com email to login as" +msgstr "L'adresse électronique login.ubuntu.com avec laquelle se connecter" + +msgid "The model assertion name" +msgstr "Le nom du modèle d'assertion" + +msgid "The output directory" +msgstr "Le répertoire de sortie" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "La recherche %q n'a renvoyé à aucun paquet Snap\n" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "Le paquet Snap à configurer (par exemple, hello-world)" + +msgid "The snap whose conf is being requested" +msgstr "Le paquet Snap dont la configuration est demandée" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "Cette commande déconnecte l'utilisateur actuel de la logithèque" + +msgid "Tool to interact with snaps" +msgstr "Outil pour interagir avec les paquets Snap" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "Transfert des profils de sécurité de %q vers %q" + +msgid "Transition ubuntu-core to core" +msgstr "Transfert d'ubuntu-core vers core" + +#, c-format +msgid "Try %q snap from %s" +msgstr "Essayer le paquet Snap %q de %s" + +msgid "Two-factor code: " +msgstr "Code à deux facteurs : " + +msgid "Unalias a manual alias or an entire snap" +msgstr "Supprime un alias manuel ou un paquet Snap entier" + +msgid "Use a specific snap revision when running hook" +msgstr "" +"Utiliser une version spécifique du paquet Snap lors de l'exécution du point " +"d'accroche" + +msgid "Use known assertions for user creation" +msgstr "Utiliser les assertions connues pour la création d'utilisateur" + +msgid "Use this channel instead of stable" +msgstr "Utiliser ce canal plutôt que le canal stable" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "ATTENTION : échec de l'activation de la connexion : %v\n" + +msgid "Waiting for server to restart" +msgstr "En attente du redémarrage du serveur" + +msgid "Watch a change in progress" +msgstr "Surveiller un changement en cours" + +msgid "Wrong again. Once more: " +msgstr "De nouveau incorrect. Encore une fois : " + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "Le fichier Xauthority n'appartient pas à l'utilisateur actuel %s" + +msgid "Yes, yes it does." +msgstr "Oui, oui c'est le cas." + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" +"Vous devez être connecté pour acheter des logiciels. Veuillez exécuter " +"« snap login » et réessayer." + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" +"Un mode de paiement doit être associé à votre compte pour pouvoir acheter un " +"paquet Snap. Allez sur https://my.ubuntu.com/payment/edit pour en ajouter " +"un.\n" +"\n" +"Ensuite, il suffit d'exécuter à nouveau « snap buy %s »." + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" +"La commande « snap changes » attend un nom de paquet Snap, essayez : « snap " +"tasks %s »" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" +"\n" +"Installer, configurer, actualiser et supprimer des paquets Snap. Ceux-ci " +"sont des\n" +"« paquets universels » qui fonctionnent sur de nombreux systèmes Linux " +"différents,\n" +"permettant la distribution sécurisée des dernières applications et des " +"derniers utilitaires pour\n" +"le nuage, les serveurs, les ordinateurs de bureau et l'internet des objets.\n" +"\n" +"Ceci est l'interface en ligne de commande pour Snapd, un service en arrière-" +"plan qui prend en charge les\n" +"paquets Snap sur le système. Commencez avec « snap list » pour voir les " +"paquets Snap installés.\n" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" +"\n" +"La commande « abort » tente d'interrompre un changement qui a encore des " +"tâches en cours.\n" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" +"\n" +"La commande « ack » essaye d'ajouter une assertion à la base de données " +"d'assertions du système.\n" +"\n" +"L'assertion peut également être une nouvelle révision d'une assertion " +"préexistante qu'elle remplacera.\n" +"\n" +"Pour réussir, l'assertion doit être valide, sa signature vérifiée avec une " +"clé publique connue et pour \n" +"laquelle l'assertion est consistante et sa condition préalable dans la base " +"de données.\n" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" +"\n" +"La commande « alias » associe le paquet Snap donné à l'alias donné.\n" +"\n" +"Une fois que cet alias manuel est défini, la commande de l'application " +"correspondante peut être invoquée en utilisant simplement l'alias.\n" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" +"\n" +"La commande « alisases » affiche la liste de tous les alias disponibles dans " +"le système et leur état.\n" +"\n" +"$ snap aliases \n" +"\n" +"Affiche uniquement les alias définis par le paquet Snap spécifié.\n" +"\n" +"Un alias est indiqué comme indéfini s'il a été explicitement activé ou " +"désactivé mais \n" +"qu'il n'est pas défini dans la version du paquet Snap en cours " +"d'utilisation ;\n" +"éventuellement temporairement (par exemple à cause d'un retour en arrière),\n" +"sinon ceci peut être résolu avec snap alias --reset.\n" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" +"\n" +"La commande « auto-import » recherche les assertions signées par des\n" +"autorités de confiance sur les appareils montés disponibles et, le cas " +"échéant, \n" +"exécute des modifications du système sur cette base.\n" +"\n" +"Si un ou plusieurs appareils sont disponibles via --mount, ils sont montés \n" +"temporairement pour être inspectés eux aussi. Même dans ce cas, la commande\n" +"prendra toujours en compte tous les appareils montés.\n" +"\n" +"Les assertions importées doivent être disponibles dans le fichier auto-" +"import.assert \n" +"à la racine du système de fichier.\n" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" +"\n" +"La commande « buy » achète un paquet Snap dans la logithèque.\n" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" +"\n" +"La commande « changes » affiche un résumé des changements récents effectués " +"sur le système." + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" +"\n" +"La commande « connect » relie un connecteur à une prise.\n" +"Elle peut être appelée de deux manières :\n" +"\n" +"$ snap connect : :\n" +"\n" +"Relie le connecteur indiqué à la prise spécifiée.\n" +"\n" +"$ snap connect : \n" +"\n" +"Relie le connecteur spécifié à la seule prise du paquet Snap qui correspond\n" +"à l'interface connectée. S'il existe plus d'une prise potentielle, la " +"commande\n" +"échoue.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connecte le connecteur indiqué dans la prise du paquet Snap principal avec " +"un nom correspondant\n" +"au nom du connecteur.\n" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" +"\n" +"La commande « create-user » crée un utilisateur local du système avec le nom " +"d'utilisateur\n" +"et les clés SSH enregistrées sur le compte de la logithèque identifié via " +"l'adresse électronique fournie.\n" +"\n" +"Un compte peut être configuré sur https://login.ubuntu.com.\n" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" +"\n" +"La commande « debug » comporte plusieurs sous-commandes supplémentaires.\n" +"\n" +"La commande debug peut être supprimée sans préavis et il se peut qu'elle\n" +"ne fonctionne pas dans les systèmes autres que de développement.\n" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" +"\n" +"La commande « disable » désactive un paquet Snap. Les binaires et les " +"services du paquet\n" +"Snap ne seront plus disponibles. Mais toutes les données seront toujours " +"disponibles et le paquet\n" +"Snap peut facilement être de nouveau activé.\n" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" +"\n" +"La commande « disconnect » déconnecte un connecteur d'une prise.\n" +"Elle peut être appelée de deux manières :\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Déconnecte le connecteur indiqué de la prise spécifiée.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Déconnecte tout ce qui est relié au connecteur indiqué ou à la prise " +"spécifiée.\n" +"Le nom du paquet Snap peut être omis pour le paquet Snap principal.\n" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" +"\n" +"La commande « download » télécharge le paquet Snap indiqué et les assertions " +"\n" +"qui lui sont nécessaires dans le répertoire en cours d'utilisation, dans des " +"fichiers\n" +"portant respectivement les extensions .snap et .assert.\n" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" +"\n" +"La commande « enable » active un paquet Snap ayant été précédemment " +"désactivé.\n" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" +"\n" +"La commande « get » affiche les options de configuration pour le paquet Snap " +"en cours d'utilisation.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"Si plusieurs noms d'options sont fournis, la commande renvoie un document :\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Les valeurs imbriquées peuvent être récupérées en utilisant un chemin séparé " +"par des \n" +"points :\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Les valeurs des paramètres de connexion à l'interface peuvent être affichés " +"avec :\n" +"\n" +" $ snapctl get :monconnecteur usb-vendor\n" +" $ snapctl get :maprise path\n" +"\n" +"Cela renvoie les paramètres donnés à partir de l'extrémité de l'interface " +"locale, qu'il s'agisse \n" +"d'une prise ou d'un connecteur. Il est également possible de récupérer le " +"paramètre de \n" +"l'extrémité du paquet Snap connecté en le demandant explicitement à l'aide " +"des options \n" +"« --plug » et « --slot » :\n" +"\n" +" $ snapctl get :monconnecteur --slot usb-vendor\n" +"\n" +"Ceci recherche le paramètre « usb-vendor » de la prise qui est connectée à " +"« monconnecteur ».\n" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" +"\n" +"La commande « get » affiche les options de configuration du paquet Snap\n" +"mentionné.\n" +"\n" +" $ snap get nom-du-snap username\n" +" frank\n" +"\n" +"Si les noms de plusieurs options sont demandés, la commande renvoie\n" +"un document :\n" +"\n" +" $ snap get nom-du-snap username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Les valeurs imbriquées peuvent être obtenues en indiquant \n" +"le chemin avec des points :\n" +"\n" +" $ snap get nom-du-snap author.name\n" +" frank\n" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" +"\n" +"La commande « help » affiche des informations utiles - plus utiles que celle-" +"ci ;)\n" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" +"\n" +"La commande « info » affiche des informations détaillées à propos d'un " +"paquet Snap, que ce soit par nom ou par chemin." + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" +"\n" +"La commande « install » installe sur le système le paquet Snap cité.\n" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" +"\n" +"La commande « interfaces » liste les interfaces disponibles dans le " +"système.\n" +"\n" +"Par défaut, toutes les prises et tous les connecteurs, utilisés et offerts " +"par tous les paquets Snap, sont affichés.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Liste uniquement les prises ou les connecteurs spécifiés.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Liste les prises offertes et les connecteurs utilisés par le paquet Snap " +"spécifié.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filtre toute la sortie de manière à ce que seuls les connecteurs et/ou les " +"prises correspondants aux détails fournis soient listés.\n" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" +"\n" +"La commande « known » affiche les assertions connues du type fourni.\n" +"Si les paires en-tête=valeur sont fournies après le type d'assertion, les " +"assertions\n" +"affichées doivent alors avoir leurs en-têtes correspondant aux valeurs " +"fournies.\n" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" +"\n" +"La commande « list » affiche un résumé des paquets Snap installés sur le " +"système actuel." + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" +"\n" +"La commande « login » authentifie vis-à-vis de Snapd et de la logithèque et " +"enregistre les\n" +"identifiants dans le fichier ~/.snap/auth.json. Les communications à venir " +"seront alors effectuées en\n" +"utilisant ces identifiants.\n" +"\n" +"La connexion marche uniquement pour des utilisateurs locaux des groupes " +"sudo, admin ou wheel.\n" +"\n" +"Un compte peut être configuré sur https://login.ubuntu.com\n" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" +"\n" +"La commande « managed » affiche vrai ou faux, selon\n" +"que Snapd ait des utilisateurs enregistrés ou pas.\n" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" +"\n" +"La commande « refresh » actualise (met à jour) le paquet Snap cité.\n" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" +"\n" +"La commande « remove » supprime du système le paquet Snap indiqué.\n" +"\n" +"Par défaut, toutes les versions du paquet Snap sont supprimées, y compris " +"leurs données et \n" +"le répertoire commun de données. Quand une option « --revision » est fournie " +"en argument, seule la\n" +"version spécifiée est supprimée.\n" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" +"\n" +"La commande « revert » rétablit le paquet Snap donné vers son état avant\n" +"la dernière actualisation. Cela réactivera la précédente révision du paquet " +"Snap et\n" +"utilisera les données d'origine qui étaient associées à cette révision,\n" +"éliminant tout changement dans les données qui aurait été effectué par la " +"dernière révision.\n" +"En tant qu'exception, les données que la paquet Snap choisit explicitement " +"de partager entre\n" +"les révisions ne sont pas touchées par le processus de rétablissement.\n" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" +"\n" +"La commande « set » change les options de configurations fournies comme " +"demandé.\n" +"\n" +" $ snap set snap-name username=frank password=$MOT-DE-PASSE\n" +"\n" +"Tous les changements de configuration sont conservés en une seule fois, et \n" +"seulement après que le point d'accroche de configuration du paquet Snap\n" +"ait été correctement exécuté.\n" +"\n" +"Les valeurs imbriquées peuvent être modifiées via l'indication d'un chemin \n" +"séparé par des points :\n" +"\n" +" $ snap set author.name=frank\n" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" +"\n" +"La commande « set » modifie les options de configuration fournies comme " +"demandé.\n" +"\n" +" $ snapctl set username=frank password=$MOT-DE-PASSE\n" +"\n" +"Tous les changements de configuration sont conservés en une seule fois et \n" +"seulement après que le point d'accroche de configuration du paquet Snap\n" +"ait été correctement exécuté.\n" +"\n" +"Les valeurs imbriquées peuvent être modifiées via l'indication d'un chemin \n" +"séparé par des points :\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Les attributs des prises et des connecteurs peuvent être définis dans les " +"points d'accroche\n" +"« prepare » et « connect » respectifs en indiquant le connecteur ou la prise " +"voulu(e) :\n" +"\n" +" $ snapctl set :monconnecteur path=/dev/ttyS0\n" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" +"\n" +"La commande « try » installe un paquet Snap non dépaqueté au sein du système " +"dans le cadre de tests.\n" +"Le contenu de celui-ci continue à être utilisé même après l'installation, " +"les modifications non relatives aux métadonnées\n" +"faites à ce niveau prennent donc effet instantanément. Les modifications de " +"métadonnées telles que celles\n" +"effectuées dans snap.yaml nécessiteront une réinstallation pour prendre " +"effet.\n" +"\n" +"Si l'argument « snap-dir » est omis, la commande « try » essayera de le " +"déduire\n" +"si soit le fichier snapcraft.yaml et le répertoire principal, soit le " +"fichier meta/snap.yaml sont trouvés\n" +"à proximité du répertoire de travail actuel.\n" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" +"\n" +"La commande « version » affiche les versions du client, du serveur et du\n" +"système d'exploitation en cours d'exécution.\n" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" +"\n" +"La commande « watch » attend que la commande « change-id » indiquée\n" +"soit terminée et affiche l'état d'avancement (si disponible).\n" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" +"\n" +"\n" +"Le dossier personnel est partagé entre Snappy et la dimension classique.\n" +"Exécutez « exit » pour quitter le shell classique.\n" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" +"un nom de paquet Snap unique est nécessaire pour spécifier les marqueurs de " +"mode ou de canal" + +msgid "a single snap name is needed to specify the revision" +msgstr "" +"un nom de paquet Snap unique est nécessaire pour spécifier la révision" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" +"le nom d'un seul paquet Snap doit être spécifié lorsque la validation est " +"ignorée" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "acheté" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "cassé" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "impossible d'acheter le paquet Snap : %v" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" +"impossible d'acheter le paquet Snap : caractères invalides dans le nom" + +msgid "cannot buy snap: it has already been bought" +msgstr "impossible d'acheter le paquet Snap : celui-ci a déjà été acheté" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "impossible de créer %q : %v" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "impossible de créer le fichier d'assertions : %v" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" +"impossible d'extraire le nom du paquet Snap à partir du fichier local %q : %v" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "impossible de trouver l'application %q dans %q" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "impossible de trouver le point d'accroche %q dans %s" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "impossible d'obtenir des données pour %q : %v" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "impossible d'obtenir le chemin complet pour %q : %v" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "impossible d'obtenir l'utilisateur actuel : %s" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "impossible d'obtenir le nom de l'utilisateur actuel : %v" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "impossible de marquer le démarrage comme réussi : %s" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "impossible d'ouvrir la base de données d'assertions : %v" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "impossible de lire l'assertion d'entrée : %v" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "impossible de lire le lien symbolique : %v" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "impossible de corriger l'application Snap %q : %v" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "impossible de signe l'assertion : %v" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "impossible de mettre à jour le lien symbolique actuel de %q : %v" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "impossible d'utiliser la clé %q : %v" + +msgid "cannot use --hook and --command together" +msgstr "Impossible d'utiliser --hook et --command ensemble" + +msgid "cannot use change ID and type together" +msgstr "impossible de changer l'identifiant et le type en même temps" + +msgid "cannot use devmode and jailmode flags together" +msgstr "impossible d'utiliser les marqueurs devmode et jailmode ensemble" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "impossible de valider le propriétaire du fichier %s" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "le changement s'est terminé au statut %q sans message d'erreur" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "utilisateur %q créé\n" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "désactivé" + +msgid "email:" +msgstr "courriel :" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "erreur : %v\n" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" +"erreur : l'argument n'a pas été fourni et n'a pas pu être déduit" + +msgid "get which option?" +msgstr "obtenir quelle option ?" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" +"les attributs d'interface ne peuvent être lus que pendant l'exécution des " +"points d'accroche d'interface" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" +"les attributs d'interface ne peuvent être définis que pendant l'exécution " +"des points d'accroche d'interface" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" +"erreur interne, veuillez le signaler : l'exécution de %q a échoué : %v\n" + +msgid "internal error: cannot find attrs task" +msgstr "erreur interne : impossible de trouver la tâche attrs" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" +"erreur interne : impossible de trouver les données de connecteur ou de prise " +"dans la tâche appropriée" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "erreur interne : impossible de trouver %s dans la tâche appropriée" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "attribut invalide : %q (clé=valeur attendu)" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "configuration invalide : %q (clé=valeur attendue)" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "filtre d'en-tête invalide: %q (clé=valeur attendu)" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "paramètre invalide: %q (clé=valeur attendu)" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "valeur invalide : %q (snap:nom ou snap attendue)" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" +"le nom de clé %q n'est pas valide ; seuls les nombres, les lettres ASCII et " +"les tirets sont autorisés" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "Snap-confine manquant : essayez de mettre à jour votre paquet Snapd" + +msgid "need the application to run as argument" +msgstr "besoin que l'application soit exécutée comme un argument" + +msgid "no changes found" +msgstr "aucun changement trouvé" + +#, c-format +msgid "no changes of type %q found" +msgstr "aucun changement trouvé pour le type %q" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "aucune interface trouvée" + +msgid "no matching snaps installed" +msgstr "aucun paquet Snap correspondant installé" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "aucun paquet Snap valide n'a été indiqué" + +msgid "not a valid snap" +msgstr "ceci n'est pas un paquet Snap valide" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "privé" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" +"redémarrage programmé pour mettre à jour le système - annulation temporaire " +"possible via « sudo shutdown -c »" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "définir quelle option ?" + +msgid "show detailed information about a snap" +msgstr "afficher les informations détaillées à propos d'un paquet Snap" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "le paquet Snap %q est déjà installé, voir « snap refresh --help »" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "le paquet Snap est gratuit" + +msgid "too many arguments for command" +msgstr "trop d'arguments pour la commande" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "trop d'arguments pour le point d'accroche %q : %s" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "trop d'arguments : %s" + +msgid "unavailable" +msgstr "indisponible" + +#, c-format +msgid "unknown attribute %q" +msgstr "attribut %q inconnu" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "commande %q inconnue, voir « snap --help »" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "prise ou connecteur %q inconnu(e)" + +#, c-format +msgid "unsupported shell %v" +msgstr "shell %v non pris en charge" + +#~ msgid "" +#~ "\n" +#~ "The change command displays a summary of tasks associated to an individual " +#~ "change." +#~ msgstr "" +#~ "\n" +#~ "La commande « change » affiche un résumé des tâches associées à un " +#~ "changement individuel." + +#~ msgid "" +#~ "\n" +#~ "The find command queries the store for available packages.\n" +#~ msgstr "" +#~ "\n" +#~ "La commande « find » interroge la logithèque au sujet des paquets " +#~ "disponibles.\n" + +#~ msgid "" +#~ "Show last change of given type (install, refresh, remove, try, auto-refresh " +#~ "etc.)" +#~ msgstr "" +#~ "Affiche les derniers changements pour un type donné (installation, " +#~ "actualisation, suppression, essai, actualisation automatique, etc.)" diff -Nru snapd-2.28.5/po/gl.po snapd-2.29.3/po/gl.po --- snapd-2.28.5/po/gl.po 2016-04-14 20:59:39.000000000 +0000 +++ snapd-2.29.3/po/gl.po 2017-10-27 12:23:38.000000000 +0000 @@ -7,517 +7,1797 @@ msgstr "" "Project-Id-Version: snappy\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2015-10-15 15:53+0200\n" -"PO-Revision-Date: 2015-10-21 16:12+0000\n" -"Last-Translator: Marcos Lans \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-10-09 21:18+0000\n" +"Last-Translator: Xosé \n" "Language-Team: Galician \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2015-10-22 05:57+0000\n" -"X-Generator: Launchpad (build 17812)\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" -#. TRANSLATORS: the %s is a pkgname, the second a comma separated list of paths #, c-format -msgid "%s: %s\n" -msgstr "%s: %s\n" +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" -#. TRANSLATORS: the %s stand for "name", "version", "description" #, c-format -msgid "%s\t%s\t%s (forks not shown: %d)\t" -msgstr "%s\t%s\t%s (bifurcacións non mostradas: %d)\t" +msgid "%q switched to the %q channel\n" +msgstr "" -#. TRANSLATORS: the first %s is a pkgname, the second %s is a path +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). #, c-format -msgid "'%s' is no longer allowed to access '%s'\n" -msgstr "«%s» xa non ten permiso de acceso a «%s»\n" +msgid "%s %s mounted from %s\n" +msgstr "%s %s montouse desde %s\n" -#. TRANSLATORS: the first %s is a pkgname, the second %s is a path #, c-format -msgid "'%s' is now allowed to access '%s'\n" -msgstr "«%s» ten permiso de acceso a «%s»\n" +msgid "%s (delta)" +msgstr "%s (delta)" -#. TRANSLATORS: the first %s is a pkgname, the second %s is a path +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) #, c-format -msgid "'%s' previously allowed access to '%s'. Skipping\n" -msgstr "«%s» permitiu o acceso previamente a «%s ». Saltando\n" +msgid "%s (see \"snap login --help\")" +msgstr "%s (ver \"snap login --help\")" -#. TRANSLATORS: the %s is a pkgname +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) #, c-format -msgid "'%s:' is not allowed to access additional hardware\n" -msgstr "«%s:» non ten permiso de acceso a hardware adicional\n" +msgid "%s (try with sudo)" +msgstr "%s (tentar con sudo)" -msgid "(deprecated) please use \"list\"" -msgstr "(obsoleto) use «list»" +#, c-format +msgid "%s already installed\n" +msgstr "%s xa está instalado\n" -msgid "2fa code: " -msgstr "Código 2fa: " +#, c-format +msgid "%s disabled\n" +msgstr "%s está desactivada\n" -msgid "" -"A concise summary of key attributes of the snappy system, such as the " -"release and channel.\n" -"\n" -"The verbose output includes the specific version information for the factory " -"image, the running image and the image that will be run on reboot, together " -"with a list of the available channels for this image.\n" -"\n" -"Providing a package name will display information about a specific installed " -"package.\n" -"\n" -"The verbose version of the info command for a package will also tell you the " -"available channels for that package, when it was installed for the first " -"time, disk space utilization, and in the case of frameworks, which apps are " -"able to use the framework." +#, c-format +msgid "%s enabled\n" +msgstr "%s está activada\n" + +#, c-format +msgid "%s not installed\n" +msgstr "%s non está instalado\n" + +#, c-format +msgid "%s removed\n" +msgstr "%s eliminada\n" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "%s revertida a %s\n" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "%s%s %s desde «%s» instalada\n" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "%s%s %s de «%s» refrescado\n" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "%s%s %s instalada\n" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "%s%s %s refrescado\n" + +msgid "--list does not take mode nor channel flags" +msgstr "--list non acepta bandeiras de modo nin de canle" + +msgid "--time does not take mode nor channel flags" msgstr "" -"Resumo conciso dos atributos fundamentais do sistema snappy, como a " -"publicación e a canle.\n" -"\n" -"A saída con información detallada inclúe a información específica da versión " -"para a imaxe de fábrica, a imaxe en execución e a imaxe que se executará ao " -"reiniciar, xunto cunha lista de canles para esta imaxe.\n" -"\n" -"Indicar un nome de paquete mostrará información sobre o paquete instalado " -"específico.\n" -"\n" -"A versión con información detallada da orde para o paquete tamén mostrará as " -"canles dispoñíbeis dese paquete, cando se instalou por primeira vez, a " -"utilización de espazo no disco e no caso de frameworks, que aplicativos usa " -"o framework." -msgid "Activate a package" -msgstr "Activar un paquete" +msgid "-r can only be used with --hook" +msgstr "-r só pode ser empregado con --hook" -msgid "" -"Activate a package that has previously been deactivated. If the package is " -"already activated, do nothing." +msgid "" msgstr "" -"Activar un paquete previamente desactivado. Se o paquete xa está activado, " -"ignorar." -msgid "" -"Allows rollback of a snap to a previous installed version. Without any " -"arguments, the previous installed version is selected. It is also possible " -"to specify the version to rollback to as a additional argument.\n" +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "Cancelar un cambio pendente" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "Todos os snaps está actualizados." + +msgid "Alternative command to run" +msgstr "Orde alternativa a executar" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" msgstr "" -"Permite a reversión dun snap a unha versión instalada anteriormente. Se non " -"se usan argumentos seleccionarase a versión instalada previamente. Tamén se " -"pode especificar a versión á que reverter como un argumento adicional.\n" -msgid "Assign a hardware device to a package" -msgstr "Asignar un dispositivo de hardware a un paquete" +msgid "Assertion file" +msgstr "" -msgid "Assign hardware to a specific installed package" -msgstr "Asignar hardware a un paquete instalado" +msgid "Assertion type name" +msgstr "" -msgid "Builds a snap package" -msgstr "Constrúe un paquete snap" +msgid "Authenticates on snapd and the store" +msgstr "" -#. TRANSLATORS: the first %q is the file that can not be read and %v is the error message #, c-format -msgid "Can't read hook file %q: %v" -msgstr "Non é posíbel ler o ficheiro do «hook» %q: %v" +msgid "Auto-refresh %d snaps" +msgstr "" -msgid "" -"Configures a package. The configuration is a YAML file, provided in the " -"specified file which can be \"-\" for stdin. Output of the command is the " -"current configuration, so running this command with no input file provides a " -"snapshot of the app's current config." +#, c-format +msgid "Auto-refresh snap %q" msgstr "" -"Configura un paquete. A configuración é un ficheiro YAML, proporcionado no " -"ficheiro específico que pode ser «-» para stdin. A saída da orde é a " -"configuración actual, de xeito que se se executa esta orde sen indicar un " -"ficheiro de entrada proporcionará unha imaxe da configuración actual do " -"aplicativo." -msgid "Creates a snap package and if available, runs the review scripts." +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" msgstr "" -"Crea un paquete snap e, se están dispoñíbeis, executa os scripts de revisión." -msgid "Deactivate a package" -msgstr "Desactivar un paquete" +msgid "Bad code. Try again: " +msgstr "Codo errado. Tente de novo: " + +msgid "Buys a snap" +msgstr "Compra unha snap" + +msgid "Change ID" +msgstr "Cambiar o identificador" + +msgid "Changes configuration options" +msgstr "Cambia as opcións de configuración" msgid "" -"Deactivate a package. If the package is already deactivated, do nothing." +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." msgstr "" -"Desactivar un paquete previamente activado. Se o paquete xa está " -"desactivado, ignorar." - -msgid "Display a summary of key attributes of the snappy system." -msgstr "Mostra un resumo dos atributos principais do sistema snappy." -msgid "Do not clean up old versions of the package." -msgstr "Non eliminar as versións antigas dos paquetes." +msgid "Command\tAlias\tNotes" +msgstr "" -msgid "Ensures system is running with latest parts" -msgstr "Asegúrase de que o sistema está en execución coa últimas partes." +msgid "Configuration value (key=value)" +msgstr "" -msgid "First boot has already run" -msgstr "Xa se executou o primeiro arranque" +msgid "Confirm passphrase: " +msgstr "Confirme a frase de paso: " -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "Generated '%s' snap\n" -msgstr "Xerado o paquete snap de «%s»\n" +msgid "Connect %s:%s to %s:%s" +msgstr "Conectar %s:%s a %s:%s" -msgid "Include information about packages from the snappy store" -msgstr "Incluír información sobre paquetes da tenda de snappy" +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" -msgid "Install a snap package" -msgstr "Instalar un paquete snap" +msgid "Constrain listing to specific interfaces" +msgstr "" -msgid "Install snaps even if the signature can not be verified." -msgstr "Instalar snaps incluso con sinaturas non comprobadas." +msgid "Constrain listing to those matching header=value" +msgstr "" -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "Installing %s\n" -msgstr "Instalando %s\n" +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" -msgid "List active components installed on a snappy system" -msgstr "Lista os compoñentes activos instalados nun sistema snappy" +msgid "Create snap build assertion" +msgstr "" -msgid "List assigned hardware device for a package" -msgstr "Lista o dispositivo de hardware asignado a un paquete" +msgid "Create snap-build assertion for the provided snap file." +msgstr "" -msgid "List assigned hardware for a specific installed package" -msgstr "Listar o hardware asignado a un paquete instalado" +msgid "Creates a local system user" +msgstr "Crea un usuario local do sistema" -msgid "Log into the store" -msgstr "Iniciar sesión na tenda" +msgid "Delete cryptographic key pair" +msgstr "" -msgid "Login successful" -msgstr "Inicio de sesión correcto" +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" -msgid "Name\tDate\tVersion\t" -msgstr "Nome\tData\tVersión\t" +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" -msgid "Name\tDate\tVersion\tDeveloper\t" -msgstr "Nome\tData\tVersión\tDesenvolvedor\t" +msgid "Disables a snap in the system" +msgstr "" -msgid "Name\tVersion\tSummary\t" -msgstr "Nome\tVersión\tResumo\t" +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "No snap: '%s' found" -msgstr "Ningún snap: atopouse «%s»" +msgid "Disconnect %s:%s from %s:%s" +msgstr "Desconectar %s:%s de %s:%s" -msgid "Password: " -msgstr "Contrasinal: " +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" -msgid "Provide information about a specific installed package" -msgstr "Proporcionar información sobre un paquete instalado" +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" msgid "" -"Provides a list of all active components installed on a snappy system.\n" -"\n" -"If requested, the command will find out if there are updates for any of the " -"components and indicate that by appending a * to the date. This will be " -"slower as it requires a round trip to the app store on the network.\n" -"\n" -"The developer information refers to non-mainline versions of a package (much " -"like PPAs in deb-based Ubuntu). If the package is the primary version of " -"that package in Ubuntu then the developer info is not shown. This allows one " -"to identify packages which have custom, non-standard versions installed. As " -"a special case, the \"sideload\" developer refers to packages installed " -"manually on the system.\n" -"\n" -"When a verbose listing is requested, information about the channel used is " -"displayed; which is one of alpha, beta, rc or stable, and all fields are " -"fully expanded too. In some cases, older (inactive) versions of snappy " -"packages will be installed, these will be shown in the verbose output and " -"the active version indicated with a * appended to the name of the component." +"Download the given revision of a snap, to which you must have developer " +"access" msgstr "" -"Proporciona unha lista dos compoñentes activos instalados nun sistema " -"snappy.\n" -"\n" -"Se se solicita, unha orde averiguará se hai actualizacións para algún dos " -"compoñentes e indicarao anexando un * á data. Isto pode ser lento xa que " -"require un proceso de ida e volta á tenda de aplicativos na rede.\n" -"\n" -"A información do desenvolvedor refírese a versións que non son da liña " -"principal dun paquete (como as PPAs de Ubuntu baseado en Debian). Se o " -"paquete é unha primeira versión do paquete en Ubuntu non se mostrará a " -"información do desenvolvedor. Isto permite identificar paquetes con versións " -"personalizadas non estándar instaladas. Como un caso especial, o " -"desenvolvedor «sideload» refírese a paquetes instalados manualmente no " -"sistema.\n" -"\n" -"Cando se solicite unha lista detallada, mostrarase a información sobre a " -"canle utilizada; que pode ser alfa, beta, rc ou estábel e todos os campos " -"estarán expandidos. Nalgúns casos, instalaranse versións antigas (inactivas) " -"dos paquetes snappy; mostraranse na saída de información detallada e a " -"versión activa indicarase cun * anexado ao nome do compoñente." -msgid "Provides more detailed information" -msgstr "Proporciona unha información máis detallada" +msgid "Downloads the given snap" +msgstr "" -msgid "Purge an installed package." -msgstr "Purga un paquete instalado." +msgid "Email address: " +msgstr "" -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "Purging %s\n" -msgstr "Purgando %s\n" +msgid "Enable %q snap" +msgstr "" -msgid "Query and modify snappy services" -msgstr "Consultar e modificar sevizos snappy" +msgid "Enables a snap in the system" +msgstr "" -msgid "Query and modify snappy services of locally-installed packages" +msgid "Entering classic dimension" msgstr "" -"Consultar e modificar sevizos snappy de paquetes instalados localmente" -msgid "Query the store for available packages" -msgstr "Consulta na tenda os paquetes dispoñíbeis" +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" -msgid "Reboot if necessary to be on the latest running system." -msgstr "Reiniciar se é necesario estar no último sistema en execución." +msgid "Export cryptographic public key" +msgstr "" -#. TRANSLATORS: the first %s is a pkgname the second a version #, c-format -msgid "Reboot to use %s version %s." -msgstr "Reiniciar para usar %s na versión %s." +msgid "Fetch and check assertions for snap %q%s" +msgstr "" -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "Reboot to use the new %s." -msgstr "Reiniciar para usar o novo %s." +msgid "Fetching assertions for %q\n" +msgstr "" -#. TRANSLATORS: the %s shows a comma separated list -#. of package names #, c-format -msgid "Rebooting to satisfy updates for %s\n" -msgstr "Reiniciando para rematar as actualizacións de %s\n" +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" -msgid "Remove a snapp part" -msgstr "Retirar unha parte snapp" +msgid "Force adding the user, even if the device is already managed" +msgstr "" -msgid "Remove all the data from the listed packages" -msgstr "Eliminar todos os datos dos paquetes da lista" +msgid "Force import on classic systems" +msgstr "" msgid "" -"Remove all the data from the listed packages. Normally this is used for " -"packages that have been removed and attempting to purge data for an " -"installed package will result in an error. The --installed option overrides " -"that and enables the administrator to purge all data for an installed " -"package (effectively resetting the package completely)." +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" msgstr "" -"Eliminar todos os datos dos paquetes da lista. Normalmente úsase para " -"paquetes eliminados que cando se tentan purgar os seus datos obtemos un " -"erro. A opción --installed anula iso e activa que o aministrador poida " -"purgar todos os datos dun paquete instalado (restabelecendo eficaz e " -"completamente o paquete)" -msgid "Remove hardware from a specific installed package" -msgstr "Eliminar hardware para un paquete instalado" +msgid "Generate the manpage" +msgstr "" -#. TRANSLATORS: the %s is a pkgname -#, c-format -msgid "Removing %s\n" -msgstr "Eliminando %s\n" +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" -msgid "Rollback to a previous version of a package" -msgstr "Reverter a unha versión anterior dun paquete" +msgid "Hook to run" +msgstr "" -msgid "Search for packages to install" -msgstr "Buscar paquetes para instalar" +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" -msgid "Set configuration for a specific installed package" -msgstr "Configurar un paquete instalado" +msgid "Identifier of the signer" +msgstr "" -msgid "Set configuration for an installed package." -msgstr "Estabelecer a configuración dun paquete instalado." +msgid "Identifier of the snap package associated with the build" +msgstr "" -msgid "Set properties of system or package" -msgstr "Estabelecer as propiedades do sistema ou paquete" +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" +#, c-format msgid "" -"Set properties of system or package\n" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" "\n" -"Supported properties are:\n" -" active=VERSION\n" -"\n" -"Example:\n" -" set hello-world active=1.0\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" msgstr "" -"Estabelece as propiedade do sistema ou paquete\n" -"\n" -"As propiedades aceptadas son:\n" -" active=VERSION\n" -"\n" -"Exemplo:\n" -" set hello-world active=1.0\n" -#. TRANSLATORS: the first %s is a pkgname, the second %s is the new version #, c-format -msgid "Setting %s to version %s\n" -msgstr "Axustando %s á versión %s\n" +msgid "Install %q snap from file %q" +msgstr "" -msgid "Show all available forks of a package" -msgstr "Mostrar todas as bifurcacións dispoñíbeis dun paquete" +msgid "Install from the beta channel" +msgstr "" -msgid "Show available updates (requires network)" -msgstr "Mostrar as actualizacións dispoñíbeis (precisa conexión á rede)" +msgid "Install from the candidate channel" +msgstr "" -msgid "Show channel information and expand all fields" -msgstr "Mostrar a información da canle e ampliar todos os campos" +msgid "Install from the edge channel" +msgstr "" -msgid "Snap\tService\tState" -msgstr "Snap\tServizo\tEstado" +msgid "Install from the stable channel" +msgstr "" -msgid "Specify an alternate output directory for the resulting package" -msgstr "Indicar un cartafol de saída alternativo para o paquete resultante" +#, c-format +msgid "Install snap %q" +msgstr "" -msgid "The Package to install (name or path)" -msgstr "Paquete a instalar (nome ou ruta)" +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" msgid "" -"The \"versions\" command is no longer available.\n" -"\n" -"Please use the \"list\" command instead to see what is installed.\n" -"The \"list -u\" (or \"list --updates\") will show you the available updates\n" -"and \"list -v\" (or \"list --verbose\") will show all installed versions.\n" +"Install the given revision of a snap, to which you must have developer access" msgstr "" -"A orde «versions» xa non está dispoñíbel.\n" -"\n" -"Use a orde «list» no seu canto para ver cal está instalada.\n" -"«list -u» (ou «list --updates») mostrará as actualizacións dispoñíbeis.\n" -"e «list -v» (ou «list --verbose») mostrará todas as versións instaladas.\n" -msgid "The configuration for the given file" -msgstr "Configuración para o ficheiro dado" +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" -msgid "The configuration for the given install" -msgstr "Configuración para a instalación dada" +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" -msgid "The hardware device path (e.g. /dev/ttyUSB0)" -msgstr "Ruta do dispositivo de hardware (p.e. /dev/ttyUSB0)" +msgid "Installs a snap to the system" +msgstr "" -msgid "The package to rollback " -msgstr "Paquete que reverter " +msgid "Key of interest within the configuration" +msgstr "" -msgid "The version to rollback to" -msgstr "Versión á que reverter" +msgid "List a change's tasks" +msgstr "" -msgid "" -"This command adds access to a specific hardware device (e.g. /dev/ttyUSB0) " -"for an installed package." +msgid "List cryptographic keys" msgstr "" -"Esta orde engade o acceso a un dispositivo de hardware específico para un " -"paquete instalado (p.e. /dev/ttyUSB0)." -msgid "This command is no longer available, please use the \"list\" command" -msgstr "Esta orde xa non está dispoñíbel, use a orde «list»" +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" -msgid "This command list what hardware an installed package can access" -msgstr "Esta orde lista o hardware ao que pode acceder un paquete instalado" +msgid "List installed snaps" +msgstr "" -msgid "This command logs the given username into the store" -msgstr "Esta orde rexistra o nome de usuario dado na tenda" +msgid "List system changes" +msgstr "" -msgid "" -"This command removes access of a specific hardware device (e.g. " -"/dev/ttyUSB0) for an installed package." +msgid "Lists aliases in the system" msgstr "" -"Esta orde elimina o acceso a un dispositivo específico de hardware (p.e " -"/dev/ttyUSB0) dun paquete instalado." -msgid "Unassign a hardware device to a package" -msgstr "Desligar un dispositivo de hardware dun paquete" +msgid "Lists interfaces in the system" +msgstr "" -msgid "Update all installed parts" -msgstr "Actualizar todas as partes instaladas" +msgid "Lists snap interfaces" +msgstr "" -msgid "Use --show-all to see all available forks." -msgstr "Usar --show-all para ver as bifurcacións dispoñíbeis." +msgid "Log out of the store" +msgstr "" -msgid "Username for the login" -msgstr "Nome de usuario para o acceso" +msgid "Login successful" +msgstr "Inicio de sesión correcto" -#. TRANSLATORS: the %s represents a list of installed appnames -#. (e.g. "apps: foo, bar, baz") #, c-format -msgid "apps: %s\n" -msgstr "aplicativos: %s\n" +msgid "Make current revision for snap %q unavailable" +msgstr "" -#. TRANSLATORS: the %s an architecture string #, c-format -msgid "architecture: %s\n" -msgstr "arquitectura: %s\n" +msgid "Make snap %q (%s) available to the system" +msgstr "" -#. TRANSLATORS: the %s is a size #, c-format -msgid "binary-size: %v\n" -msgstr "tamaño do binario: %v\n" +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" -#. TRANSLATORS: the %s is a channel name #, c-format -msgid "channel: %s\n" -msgstr "canle: %s\n" +msgid "Make snap %q unavailable to the system" +msgstr "" -#. TRANSLATORS: the %s is a size #, c-format -msgid "data-size: %s\n" -msgstr "data-tamaño: %s\n" +msgid "Make snap %q%s available to the system" +msgstr "" -#. TRANSLATORS: the %s is a comma separated list of framework names -#, c-format -msgid "frameworks: %s\n" -msgstr "contornos de traballo: %s\n" +msgid "Mark system seeded" +msgstr "" -#. TRANSLATORS: the %s is a date #, c-format -msgid "installed: %s\n" -msgstr "instalado: %s\n" +msgid "Mount snap %q%s" +msgstr "" -msgid "package name is required" -msgstr "precísase o nome do paquete" +msgid "Name of key to create; defaults to 'default'" +msgstr "" -msgid "produces manpage" -msgstr "produce unha manpage" +msgid "Name of key to delete" +msgstr "" -#. TRANSLATORS: the %s release string -#, c-format -msgid "release: %s\n" -msgstr "publicación: %s\n" +msgid "Name of key to export" +msgstr "" -msgid "" -"snappy autopilot triggered a reboot to boot into an up to date system -- " -"temprorarily disable the reboot by running 'sudo shutdown -c'" +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " msgstr "" -"snappy autopilot activou un reinicio para comezar nun sistema actualizado -- " -"desactive temporalmente o reinicio executando «sudo shutdown -c»" -#. TRANSLATORS: the first %s is the package name, the second is the service name; the %v is the error #, c-format -msgid "unable to disable %s's service %s: %v" -msgstr "Non foi posíbel desactivar o servizo de %s %s: %v" +msgid "Password of %q: " +msgstr "" -#. TRANSLATORS: the first %s is the package name, the second is the service name; the %v is the error +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. #, c-format -msgid "unable to enable %s's service %s: %v" -msgstr "non foi posíbel activar o servizo para %s %s: %v" +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" #, c-format -msgid "unable to get logs: %v" -msgstr "non foi posíbel obter os rexistros: %v" +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" -#. TRANSLATORS: the first %s is the package name, the second is the service name; the %v is the error #, c-format -msgid "unable to start %s's service %s: %v" -msgstr "non foi posíbel iniciar o servizo para %s %s: %v" +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" -#. TRANSLATORS: the first %s is the package name, the second is the service name; the %v is the error #, c-format -msgid "unable to stop %s's service %s: %v" -msgstr "non foi posíbel parar o servizo para %s %s: %v" +msgid "Prepare snap %q (%s)" +msgstr "" -#. TRANSLATORS: the %s is a date #, c-format -msgid "updated: %s\n" -msgstr "actualizado: %s\n" +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" -#. TRANSLATORS: the %s is a version string #, c-format -msgid "version: %s\n" -msgstr "versión: %s\n" +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/hr.po snapd-2.29.3/po/hr.po --- snapd-2.28.5/po/hr.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/hr.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,2161 @@ +# Croatian translation for snapd +# Copyright (c) 2017 Rosetta Contributors and Canonical Ltd 2017 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-10-16 16:20+0000\n" +"Last-Translator: Ante Karamatić \n" +"Language-Team: Croatian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" +"%q ne sadrži otpakirani snap paket.\n" +"\n" +"Pokušajte \"snapcraft prime\" u vašem direktoriju projekta, zatim ponovno " +"\"snap try\"." + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "%s %s montiran na %s\n" + +#, c-format +msgid "%s (delta)" +msgstr "%s (delta)" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "%s (pokrenite naredbu \"snap login --help\")" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "%s (pokušajte sa sudo)" + +#, c-format +msgid "%s already installed\n" +msgstr "%s je već instaliran\n" + +#, c-format +msgid "%s disabled\n" +msgstr "%s onemogućen\n" + +#, c-format +msgid "%s enabled\n" +msgstr "%s omogućen\n" + +#, c-format +msgid "%s not installed\n" +msgstr "%s nije instaliran\n" + +#, c-format +msgid "%s removed\n" +msgstr "%s uklonjen\n" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "%s je vraćen na %s\n" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "%s%s %s sa '%s' instaliran\n" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "%s%s %s sa '%s' osvježen\n" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "%s%s %s instairan\n" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "%s%s %s osvježeno\n" + +msgid "--list does not take mode nor channel flags" +msgstr "--list ne uzima načni ni oznake kanala" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "-r se može samo koristiti s --hook" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr ":" + +msgid ":" +msgstr ":" + +msgid ":" +msgstr ":" + +msgid "Abort a pending change" +msgstr "Prekini očekivanu promjenu" + +msgid "Added" +msgstr "Dodano" + +msgid "Adds an assertion to the system" +msgstr "Dodaj potvrde u sustav" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "Pseudonim za --dangerous (DEPRECATED)" + +msgid "All snaps up to date." +msgstr "Svi snap paketi su nadopunjeni." + +msgid "Alternative command to run" +msgstr "Zamjenska naredba za pokretanje" + +msgid "Always return document, even with single key" +msgstr "Uvijek vrati dokument, čak i s jednim ključem" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" +"Adresa e-pošte korisnika na login.ubuntu.com može sadržvati više adresa e-" +"pošte" + +msgid "Assertion file" +msgstr "Datoteka potvrde" + +msgid "Assertion type name" +msgstr "Naziv vrste potvrde" + +msgid "Authenticates on snapd and the store" +msgstr "Ovjera na snapd i trgovini" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "Automatski osvježi %d snap paketa" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "Automatski osvježi %q snap paket" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "Automatski osvježi snap pakete %s" + +msgid "Bad code. Try again: " +msgstr "Netočan kôd. Pokušajte ponovno: " + +msgid "Buys a snap" +msgstr "Kupovanje snap paketa" + +msgid "Change ID" +msgstr "ID promjene" + +msgid "Changes configuration options" +msgstr "Promjene mogućnosti podešavanja" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" +"Klasične dimenzije su onemogućene na ovom sustavu.\n" +"Koristite \"sudo snap install --devmode classic && sudo classic.create\" " +"kako bi ih omogućili." + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "Vrijednost podešavanja (ključ=vrijednost)" + +msgid "Confirm passphrase: " +msgstr "Potvrdi lozinku: " + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "Povezivanje %s:%s s %s:%s" + +msgid "Connects a plug to a slot" +msgstr "Povezuje priključak u priključnicu" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "Ograniči prikazivanje na određeni snap paket ili snap:naziv" + +msgid "Constrain listing to specific interfaces" +msgstr "Ograniči prikazivanje na određeno sučelje" + +msgid "Constrain listing to those matching header=value" +msgstr "Ograniči prikazivanje onima koji odgovaraju zaglavlje=vrijednost" + +#, c-format +msgid "Copy snap %q data" +msgstr "Kopiraj podatke %q snap paketa" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" +"Stvori kriptografski par ključa koji se može koristiti za potpisivanje " +"potvrda." + +msgid "Create cryptographic key pair" +msgstr "Stvori kriptografski par ključa" + +msgid "Create snap build assertion" +msgstr "Stvori potvrdu izgradnje snap paketa" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "Stvori snap-build potvrdu za pruženu snap datoteku." + +msgid "Creates a local system user" +msgstr "Stvara korisnika lokalnog sustava" + +msgid "Delete cryptographic key pair" +msgstr "Obriši kriptografski par ključa" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "Obriši kriptografski par ključa sa zadanim nazivom." + +#, c-format +msgid "Disable %q snap" +msgstr "Onemogući %q snap paket" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "Onemogući pseudonime za snap paket %q" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "Onemogućuje snap pakete na sustavu" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "Poništava sučelje povezivanja za snap paket %q (%s)" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "Prekidanje povezivanja %s:%s sa %s:%s" + +msgid "Disconnects a plug from a slot" +msgstr "Prekidanje povezivanja priključka s priključnice" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "Ne čekaj da radnja završi samo prikaži id promjene." + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "Preuzimam snap paket %q%s s kanala %q" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" +"Preuzimanje zadane revizije snap paketa, za koji morate imati razvijateljski " +"pristup" + +msgid "Downloads the given snap" +msgstr "Preuzima zadani snap paket" + +msgid "Email address: " +msgstr "Adresa e-pošte: " + +#, c-format +msgid "Enable %q snap" +msgstr "Omogući %q snap paket" + +msgid "Enables a snap in the system" +msgstr "Omogući snap pakete u sustavu" + +msgid "Entering classic dimension" +msgstr "Ulaz u klasičnu dimenziju" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" +"Izvezi tijelo potvrde javnog ključa koji će možda biti uvezen na drugim " +"sustavima." + +msgid "Export cryptographic public key" +msgstr "Izvezi kriptografski javni ključ" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "Preuzmi i provjeri potvrdu za snap paket %q%s" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "Preuzimanje potvrde za %q\n" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "Preuzimanje snap paketa %q\n" + +msgid "Filename of the snap you want to assert a build for" +msgstr "Naziv datoteke snap paketa za koji želite potvrditi izgradnju" + +msgid "Finds packages to install" +msgstr "Traži pakete za instalaciju" + +msgid "Force adding the user, even if the device is already managed" +msgstr "Prisilno dodavanje korisnika, čak i ako se uređajem već upravlja" + +msgid "Force import on classic systems" +msgstr "Prisili uvoz na klasičnim sustavima" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" +"Formatiraj materijal javnog ključa kako je zahtijevano za ključ-računa za " +"ovaj id-računa" + +msgid "Generate device key" +msgstr "Generiraj ključ uređaja" + +msgid "Generate the manpage" +msgstr "Generieaj man stranice" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "Ocjeni stanje kvalitete izgradnje snap paketa (zadano je 'stable')" + +msgid "Grant sudo access to the created user" +msgstr "Dopusti sudo pristup stvorenom korisniku" + +msgid "Help" +msgstr "Pomoć" + +msgid "Hook to run" +msgstr "Zakači za pokretanje" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "Identifikator potpisnika" + +msgid "Identifier of the snap package associated with the build" +msgstr "Identifikator snap paketa pridružen u izgradnji" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "Zanemari provjeru od blokiranja osvježvanja drugih snap paketa" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" +"Kako bi kupili %q, morate se složiti sa najnovijim uvjetima i odredbama. " +"Posjetite https://my.ubuntu.com/payment/edit kako bi to učinili.\n" +"\n" +"Jednom kada je završeno, vratite se ovdje i pokrenite 'kupi snap paket %s' " +"ponovno." + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" +"Uključi opširan popis bilješki snap paketa (u suprotnome, sažmi bilješke)" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "Pokreni uređaj" + +msgid "Inspects devices for actionable information" +msgstr "Provjeri uređaj za korisne informacije" + +#, c-format +msgid "Install %q snap" +msgstr "Instaliraj %q snap paket" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "Instaliraj %q snap paket iz %q kanala" + +#, c-format +msgid "Install %q snap from file" +msgstr "Instaliraj %q snap paket iz datoteke" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "Instaliraj %q snap paket iz datoteke %q" + +msgid "Install from the beta channel" +msgstr "Instaliraj iz beta kanala" + +msgid "Install from the candidate channel" +msgstr "Instaliraj iz candidate kanala" + +msgid "Install from the edge channel" +msgstr "Instaliraj iz edge kanala" + +msgid "Install from the stable channel" +msgstr "Instaliraj iz stable kanala" + +#, c-format +msgid "Install snap %q" +msgstr "Instal snap paket %q" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "Instaliraj snap pakete %s" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" +"Instalirajte zadanu reviziju snap paketa, za koju morate imati " +"razvijateljski pristup" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" +"Instaliraj zadanu snap datoteku čak i ako ne postoje predpotvrđeni potpisi " +"za nju, to znači da nije provjerena i može biti opasna (--devmode to " +"podrazumijeva)" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "Instalira snap paket na sustav" + +msgid "Key of interest within the configuration" +msgstr "Ključ interesa s podešavanjem" + +msgid "List a change's tasks" +msgstr "Popis zadatka kanala" + +msgid "List cryptographic keys" +msgstr "Popis kriptografskih ključeva" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" +"Popis kriptografskih ključeva koji se mogu koristiti za potvrđivanje." + +msgid "List installed snaps" +msgstr "Popis instaliranih snap paketa" + +msgid "List system changes" +msgstr "Popis promjena sustava" + +msgid "Lists aliases in the system" +msgstr "Popis pseudonima u sustavu" + +msgid "Lists interfaces in the system" +msgstr "Popis sučelja u sustavu" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "Odjavi se iz trgovine" + +msgid "Login successful" +msgstr "Uspješna prijava" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "Učini trenutnu reviziju za snap paket %q nedostupnom" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "Učini snap paket %q (%s) dostupnim na sustavu" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "Učini snap paket %q (%s) nedostupnim na sustavu" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "Učini snap paket %q nedostupnim na sustavu" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "Učini snap paket %q%s dostupnim na sustavu" + +msgid "Mark system seeded" +msgstr "Označi sustav zasijanim" + +#, c-format +msgid "Mount snap %q%s" +msgstr "Montiram snap paket %q%s" + +msgid "Name of key to create; defaults to 'default'" +msgstr "Naziv ključa za stvaranje; zadano je 'default'" + +msgid "Name of key to delete" +msgstr "Naziv ključa za brisanje" + +msgid "Name of key to export" +msgstr "Naziv ključa za izvoz" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" +"Naziv GnuPG ključa za korištenje (zadano je 'default' kao naziv ključa)" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "Naziv ključa za korištenje, u suprotnome koristi zadani ključ" + +msgid "Name\tSHA3-384" +msgstr "Naziv\tSHA3-384" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "Naziv\tInačica\tRazvijatelj\tBilješke\tSažetak" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "Naziv\tInačica\tRevizija\tRazvijatelj\tBilješke" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" +"Još nema instaliranih snap paketa. Pokušajte \"snap install hello-world\"." + +msgid "No snaps to auto-refresh found" +msgstr "Nema pronađenog automatskog osvježvanja snap paketa" + +msgid "Output results in JSON format" +msgstr "Izlazni rezultati u JSON formatu" + +msgid "Passphrase: " +msgstr "Lozinka: " + +#, c-format +msgid "Password of %q: " +msgstr "Lozinka od %q:amp> " + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" +"Ponovno upišite vašu Ubuntu One lozinku za kupnju %q od %q\n" +"za %s. Pritisnite ctrl-c za odustajanje." + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "Pripremi snappy sliku" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "Pripremanje snap paketa %q (%s)" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "Pripremanje snap paketa %q%s" + +msgid "Print the version and exit" +msgstr "Prikaži inačicu i zatvori" + +msgid "Prints configuration options" +msgstr "Prikazuje mogućnosti podešavanja" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "Prikaži kako se upravlja sustavom" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "Postavi snap u klasičan način i onemogući sigurnosno ograničenje" + +msgid "Put snap in development mode and disable security confinement" +msgstr "Postavi snap u razvojni način i onemogući sigurnosno ograničenje" + +msgid "Put snap in enforced confinement mode" +msgstr "Postavi snap u način prisilne izolacije" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "Osvježavam %q snap paket" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "Osvježavam %q snap paket iz %q kanala" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "Osvježi sve snap pakete: bez nadopuna" + +#, c-format +msgid "Refresh snap %q" +msgstr "Osvježvam snap paket %q" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "Osvježvam snap pakete %s" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "Osvježi sve snap pakete %s: bez nadopuna" + +msgid "Refresh to the given revision" +msgstr "Osvježi na zadanu reviziju" + +msgid "Refreshes a snap in the system" +msgstr "Osvježavanje snap paketa na sustavu" + +#, c-format +msgid "Remove %q snap" +msgstr "Uklanjam %q snap paket" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "Uklanjam pseudonime za snap paket %q" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "Uklanjam podatke za snap paket %q (%s)" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "Uklanjam samo zadanu reviziju" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "Uklanjam sigurnosni profil za snap paket %q (%s)" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "Uklanjam sigurnosni profil od snap paketa %q" + +#, c-format +msgid "Remove snap %q" +msgstr "Uklanjam snap paket %q" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "Uklanjam snap paket %q (%s) iz sustava" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "Uklanjam snap pakete %s" + +msgid "Removed" +msgstr "Uklonjeno" + +msgid "Removes a snap from the system" +msgstr "Uklanja snap pakete iz sustava" + +msgid "Request device serial" +msgstr "Zahtjevam serijski uređaj" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "Ograniči pretragu u zadanom odjeljku" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "Vrati %q snap paket" + +msgid "Reverts the given snap to the previous state" +msgstr "Vraća zadani snap paket na prijašnje stanje" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "Pokreni ljusku umjesto naredbe (korisno za otklanjanje grešaka)" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "Pokreni podešavanje za %q snap paket" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "Pokreni podešavanje za %q snap paket ako je prisutan" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "Pokreni podešavanje %s za snap paket %q" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "Pokreni pripremanj uređaja" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "Pokreni zadanu naredbu snap paketa" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" +"Pokreni zadanu naredbu snap paketa sa ispravnim ograničenjem i okruženjem" + +msgid "Runs debug commands" +msgstr "Pokreće naredbe otklanjanja grešaka" + +msgid "Search private snaps" +msgstr "Pretraga privatnih snap paketa" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "Postavljanje privatnih pseudonima za snap paket %q" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "Postavljam snap paket %q (%s) sigurnosne profile" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "Postavljam snap paket %q pseudonime" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "Postavljam snap paket %q%s sigurnosne profile" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "Postavljam snap paket %q%s sigurnosne profile (faza 2)" + +msgid "Show all revisions" +msgstr "Prikaži sve revizije" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "Prikazuje poznate potvrde pružanih vrsta" + +msgid "Shows version details" +msgstr "Prikazuje pojedinosti inačice" + +msgid "Sign an assertion" +msgstr "Potpiši potvrdu" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" +"Potpisivanje potvrde koristi određeni ključ, koristeći ulaz za zaglavlje iz " +"JSON mapiranja pružanog putem stdin, tijelo potvrde se može odrediti putem " +"\"body\" pseudo zaglavlja.\n" + +msgid "Slot\tPlug" +msgstr "Priključak\tPriključnica" + +msgid "Snap name" +msgstr "Naziv snap paketa" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" +"Nažalost, vaš način plaćanja je odbijen od strane izdavača. Provjerite svoje " +"pojedinosti\n" +"plaćanja na https://my.ubuntu.com/payment/edit i pokušajte ponovno." + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "Pokrećem snap %q (%s) uslugu" + +#, c-format +msgid "Start snap %q%s services" +msgstr "Pokrećem snap %q%s usluge" + +msgid "Start snap services" +msgstr "Pokrećem snap usluge" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "Zaustavljam snap %q (%s) usluge" + +#, c-format +msgid "Stop snap %q services" +msgstr "Zaustavljam snap %q usluge" + +msgid "Stop snap services" +msgstr "Zaustavljam snap usluge" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "Ograniči upisivanje nula i znakova navoda" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "Prebacivam snap paket %q iz %s na %s" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "Privremeno montiram uređaj prije provjere" + +msgid "Tests a snap in the system" +msgstr "Testira snap paket u sustavu" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" +"Hvala što ste kupili %q. Sada možete aplikaciju instalirati na bilo koji vaš " +"uređaj\n" +"sa 'snap install %s'." + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" +"Za dobivanje podešavanje naredbi prikaza i postavki sučelja povezivanja." + +msgid "The login.ubuntu.com email to login as" +msgstr "Adresa e-pošte pri login.ubuntu.com" + +msgid "The model assertion name" +msgstr "Naziv modela potvrđivanja" + +msgid "The output directory" +msgstr "Izlazni direktorij" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "Pretraga %q je vratila 0 snap paketa\n" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "Snap paket za podešavanje (npr. hello-world)" + +msgid "The snap whose conf is being requested" +msgstr "Snap paket čije je podešavanje zatraženo" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "Ova naredba prijavljuje trenutnog korisnika izvan trgovine" + +msgid "Tool to interact with snaps" +msgstr "Alat za interakciju sa snap paketima" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "Tranzicija sigurnosnih profila iz %q u %q" + +msgid "Transition ubuntu-core to core" +msgstr "Tranzicija ubuntu-core u core" + +#, c-format +msgid "Try %q snap from %s" +msgstr "Pokušavam %q snap paket iz %s" + +msgid "Two-factor code: " +msgstr "Dvofaktorni kôd: " + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "Koristi određenu snap reviziju pri pokretanju kvačenja" + +msgid "Use known assertions for user creation" +msgstr "Koristi poznate potvrde za stvaranje korisnika" + +msgid "Use this channel instead of stable" +msgstr "Koristi ovaj kanal umjesto stabilnog" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "UPOZORENJE: neuspješno aktiviranje zapisivanje: %v\n" + +msgid "Waiting for server to restart" +msgstr "Čekanje ponovnog pokretanja poslužitelja" + +msgid "Watch a change in progress" +msgstr "Čekanje pomjena u tijeku" + +msgid "Wrong again. Once more: " +msgstr "Ponovno pogrešno. Još jednom: " + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "Da, da sada radi." + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" +"Morate biti prijavljeni za kupovnu softvera. Pokrenite 'snap login' i " +"pokušajte ponovno." + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" +"Morate imati način plaćanja pridružen vašem računu kako bi mogli kupovati " +"snap pakete, posjetite https://my.ubuntu.com/payment/edit za dodavanje " +"načina plaćanja.\n" +"\n" +"Jednom kada ste dodali svoje pojedinosti plaćanja, morate ponovno pokrenuti " +"'snap buy %s'." + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" +"\n" +"Instalirajte, podesite, osvježite i uklonite snap pakete. Snap paketi\n" +"su 'univerzalni' paketi koji rade na različitim Linux sustavima,\n" +"omogućavaju sigurno distribuiranje najnovijih aplikacija i alata za\n" +"oblak, poslužitelje, osobna računala i internetske stvari.\n" +"\n" +"Ovo je sučelje naredbenog redka za snapd, pozadinsku uslugu koja se brine o " +"snap\n" +"paketima na sustavu. Pokrenite 'snap list' kako bi vidjeli instalirane snap " +"pakete.\n" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" +"\n" +"abort - naredba pokušava prekinuti promjenu koja je još uvijek na čekanju.\n" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" +"\n" +"ack - naredba pokušava dodati potvrdu u bazu podataka potvrda sustava.\n" +"\n" +"Potvrda može biti novije izdanje prvobitne potvrde koja će se zamijeniti.\n" +"\n" +"Kako bi uspjela potvrda mora biti valjana, njen potpis provjeren s poznatim\n" +"javnim ključem i potvrda mora biti nepromjenjiva u skladu s preduvjetom\n" +"u bazi podataka.\n" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" +"\n" +"aliases - naredba prikazuje sve pseudonime dostupne na sustavu i njihovo " +"stanje.\n" +"\n" +"$ snap aliases \n" +"\n" +"Prikazuje samo pseudonime određene navedenim snap paketom.\n" +"\n" +"Pseudonim označen kao neodređen, znači da je izričito omogućen ili " +"onemogućen\n" +"ali nije određen u trenutnoj reviziji snap paketa; najvjerojatnije " +"privremeno (npr.\n" +"zbog vraćanje na prijašnje izdanje), Ako nije to, može se obrisati sa 'snap " +"alias --reset'.\n" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" +"\n" +"auto-import - naredba pretražuje dostupne montirane uređaje tražeći\n" +"potvrde koje su potpisane od strane pouzdanih nadležnih tijela,\n" +"i potencijalno obavljaju promjene na sustavu temeljenu na njima.\n" +"\n" +"Ako je dostupna jedna ili više putanja uređaja putem --mount, to su " +"privremeno\n" +"montirani uređaji koji će isto tako biti provjereni. Čak i u tom slučaju " +"naredba\n" +"će još uvijek podrazumijevati provjeravanje svih montiranih uređaja.\n" +"\n" +"Uvezene potvrde moraju biti omogućene u auto-import.assert datoteci\n" +"u korijenskom direktoriju datotečnog sustava.\n" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" +"\n" +"buy - naredba kupuje snap paket iz trgovine.\n" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" +"\n" +"changes - naredba prikazuje sažetak nedavno izvedenih promjena sustava." + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" +"\n" +"connect- naredba povezuje priključak s priključnicom.\n" +"Može se pozvati na sljedeće načine:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Povezuje određen priključak sa zadanom priključnicom.\n" +"\n" +"$ snap connect : \n" +"\n" +"Povezuje određeni priključak sa jedinom priključnicom u zadanom snap paketu " +"koji se podudara\n" +"s povezanim sučeljem. Ako postoji više od jedne potencijalne priključnice, " +"naredba se ne izvršava.\n" +"\n" +"$ snap connect :\n" +"\n" +"Povezuje određeni priključak sa priključnicom u jezgri snap paketa pomoću " +"podudaranja\n" +"naziva priključka.\n" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" +"\n" +"create-user naredba stvara lokalni sustav korisnika s korisničkim imenom i " +"SSH\n" +"ključevima registriranim u računu trgovine i indentificiranim navedenom " +"adresom e-pošte.\n" +"\n" +"Račun se može podesiti na https://login.ubuntu.com.\n" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" +"\n" +"debug - naredba sadrži odabir dodatnih podnaredbi.\n" +"\n" +"Debug naredbe se mogu ukloniti bez najave i koriste se uglavnom\n" +"na razvojnim sustavima.\n" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" +"\n" +"disable - naredba onemogućuje snap paket. Binarne datoteke i usluge\n" +"snap paketa više neće biti dostupne. Ali svi podaci su još uvijek dostupni\n" +"i snap paket se s lakoćom može ponovno omogućiti.\n" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" +"\n" +"disconnect - naredba prekida povezivanje priključka s priključnicom.\n" +"Može se pozvati na sljedeće načine:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Prekida povezivanje određenog priključka s određene priključnice.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Prekida povezivanje svega sa zadanog priključka ili priključnice.\n" +"Naziv snap paketa može biti izostavljen iz jezgre snap paketa.\n" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" +"\n" +"download - naredba preuzima zadan snap paket i njegove prateće potvrde\n" +"u zadani direktorij pod .snap i .assert datotečnim nastavkom.\n" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" +"\n" +"enable - naredba omogućuje snap paket koji je prije onemogućen.\n" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" +"\n" +"get - naredba prikazuje naredbe podešavanja za trenutni snap paket.\n" +"\n" +" $ snapctl get username\n" +" goran\n" +"\n" +"Ako je zadano više naziva mogućnosti, dokument je vraćen:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"goran\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Vrijednosti smještene unutar mogu se preuzeti putem točkaste putanje:\n" +"\n" +" $ snapctl get author.name\n" +" goran\n" +"\n" +"Vrijednosti postavki sučelja povezivanja mogu se prikazati sa:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"Ovo će vratiti navedene postavke iz krajnje točke lokalnog sustava, za " +"priključak\n" +"ili priključnicu. Vraćanje postavki iz krajnje točke povezanih snap paketa " +"je isto tako moguće\n" +"izričitim zahtjevom putem --plug i --slot mogućnostima narebenog redka:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"Ovo zahtijeva \"usb-vendor\" postavku iz priključnice koja je povezana na " +"\"myplug\".\n" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" +"\n" +"get - naredba prikazuje mogućnosti podešavanja za navedeni snap paket.\n" +"\n" +" $ snap get snap-name username\n" +" goran\n" +"\n" +"Ako je zadano više naziva mogućnosti, dokument je vraćen:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"goran\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Vrijednosti smještene unutar mogu se preuzeti putem točkaste putanje:\n" +"\n" +" $ snap get snap-name author.name\n" +" goran\n" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" +"\n" +"help - naredba prikazuje korisne informacije. Za razliku od ovoga. ;-)\n" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" +"\n" +"info - naredba prikazuje opširnije informacije o snap paketu, prema nazivu " +"ili putanji." + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" +"\n" +"install - naredba instalira odabarani snap paket na sustav.\n" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" +"\n" +"interfaces - naredba prikazuje sučelja dostupna na sustavu.\n" +"\n" +"Po zadanome sve priključnice i priključci, korišteni i ponuđeni snap " +"paketima, su prikazani.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Prikazuje samo određene priključnice i priključke.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Prikazuje ponuđene priključnice i korištene priključke od strane određenog " +"snap paketa.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filtri završavaju izlaz stoga samo priključci i/ili priključnice koje se " +"podudaraju pružanim pojedinosti su prikazani.\n" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" +"\n" +"known - naredba prikazuje poznate potvrde pružanih vrsta.\n" +"Ako je zaglavlje=vrijednost par pružan nakon vrste potvrde, prikazi potvrda " +"isto\n" +"moraju imati određena zaglavalja koja se podudaraju s pruženim " +"vrijednostima.\n" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" +"\n" +"list - naredba prikazuje sažetak snap paketa instaliranih na trenutnom " +"sustavu." + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" +"\n" +"login - naredba služi za ovjeru na snapd i snap trgovini i sprema " +"vjerodajnice\n" +"u ~/.snap/auth.json datoteku. Buduća komunikacija sa snapd biti će " +"uspostavljena\n" +"pomoću tih vjerodajnica.\n" +"\n" +"Prijava samo radi za lokalne korisnike sa 'sudo', admin ili wheel grupama.\n" +"\n" +"Račun se može podesiti na https://login.ubuntu.com\n" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" +"\n" +"managed - naredba će prikazati 'true' ili 'false' informirajući vas\n" +"ima li snapd registriranih korisnika.\n" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" +"\n" +"refresh - naredba osvježava (nadopunjuje) navedene snap pakete.\n" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" +"\n" +"remove - naredba uklanja navedene snap pakete iz sustava.\n" +"\n" +"Po zadanome sva izdanja snap paketa su uklonjena, uključujći njihove podatke " +"i zajedničke\n" +"direktorije podataka. Kada je --revision mogućnost proslijeđena samo je " +"određena revizija\n" +"uklonjena.\n" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" +"\n" +"revert - naredba vraća odabrani snap paket na stanje prije\n" +"posljednjeg osvježavanja. To će reaktivirati prijašnju reviziju snap " +"paketa,\n" +"i koristit će se izvorni podaci koji su povezani s tom revizijom,\n" +"odbacivanjem bilo kakvih promjena podataka koja su učinjena posljednjom " +"revizijom.\n" +"Kao iznimka, podaci koje snap paket izričito odabere za dijeljenje preko\n" +"revizija nisu obuhvaćeni procesom vraćanja.\n" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" +"\n" +"set - naredba mijenja pružene mogućnosti podešavanja kao što je " +"zahtijevano.\n" +"\n" +" $ snap set snap-name username=goran password=$PASSWORD\n" +"\n" +"Sve promjene podešavanja su ustrajane odjednom, i to samo ako se\n" +"postupak podešavanja vrati uspješno.\n" +"\n" +"Vrijednosti smještene unutar mogu se preuzeti putem točkaste putanje:\n" +"\n" +" $ snap set author.name=goran\n" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" +"\n" +"set - naredba mijenja pružene mogućnosti podešavanja kao što je " +"zahtijevano.\n" +"\n" +" $ snap set snap-name username=goran password=$PASSWORD\n" +"\n" +"Sve promjene podešavanja su ustrajane odjednom, i to samo ako se\n" +"postupak podešavanja vrati uspješno.\n" +"\n" +"Vrijednosti smještene unutar mogu se preuzeti putem točkaste putanje:\n" +"\n" +" $ snap set author.name=goran\n" +"\n" +"Svojstva priključka i priključnice mogu se postaviti odgovarajućom pripremom " +"i povezivanjem zakvačiti\n" +"imenovanjem odgovarajućeg priključka ili priključnice:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" +"\n" +"try - naredba instalira raspakirane snap paket na sustav u svrhu " +"testiranja.\n" +"Raspakirani sadržaj snap paketa nastavlja se koristiti čak i nakon " +"instalacije, stoga\n" +"promjene bez metapodataka su aktivne odmah. Promjene metapodataka poput " +"onih\n" +"obavljenih u snap.yaml će zahtijevati ponovnu instalaciju kako bi se odmah " +"aktivirale.\n" +"\n" +"Ako je 'snap-dir' argument izostavljen, try naredba će pokušati zaključiti " +"može\n" +"li se snapcraft.yaml datoteka i glavni direktorij ili meta/snap.yaml " +"datoteka\n" +"pronaći u odnosu na trenutni radni direktorij.\n" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" +"\n" +"version - naredba prikazuje inačicu trenutno pokrenutog klijenta, " +"poslužitelja,\n" +"i operativnog sustava.\n" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" +"\n" +"watch - naredba čeka zadani id-promjene za završetak i prikazuje napredak\n" +"(ako je dostupno).\n" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" +"\n" +"\n" +"Osobni direktorij je dijeljen između snappy i klasične dimenzije.\n" +"Pokrenite \"exit\" za napuštanje klasične ljuske.\n" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" +"pojedinačni naziv snap paketa je potreban za određivanje oznake kanala ili " +"načina" + +msgid "a single snap name is needed to specify the revision" +msgstr "pojedinačni naziv snap paketa je potreban za određivanje revizije" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" +"pojedinačni naziv snap paketa mora biti određen pri zanemarivanju provjere" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "kupljeno" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "slomljeno" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "nemogće je kupiti snap paket: %v" + +msgid "cannot buy snap: invalid characters in name" +msgstr "nemogće je kupiti snap paket: neispravni znakovi u nazivu" + +msgid "cannot buy snap: it has already been bought" +msgstr "nemogće je kupiti snap paket: već je kupljen" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "nemoguće je stvoriti %q: %v" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "nemoguće je stvoriti datoteku potvrde: %v" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "nemoguće je izdvojiti snap-naziv iz lokalne datoteke %q: %v" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "nemoguće je pronaći aplikaciju %q in %q" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "nemoguće je pronaći kvačilo %q u %q" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "nemoguće je nabaviti podatak za %q: %v" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "nemoguće je nabaviti punu putanju za %q: %v" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "nemoguće je dobiti trenutnog korisnika %v" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "nemoguće je označiti pokretanje uspješnim: %s" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "nemoguće je otvaranje baze podataka potvrda: %v" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "nemoguće čitanje ulaza potvrde: %v" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "nemoguće čitanje simboličke poveznice: %v" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "nemoguće razrješvanje snap aplikacije %q: %v" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "nemoguće potpisivanje potvrde: %v" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "nemoguća nadopuna 'trenutne' simboličke poveznice za %q: %v" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "nemoguće korištenje %q ključa: %v" + +msgid "cannot use --hook and --command together" +msgstr "nemoguće korištenje --hook i --command argumenta zajedno" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "nemoguće korištenje devmode i jailmode oznaka zajedno" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "promjena završetka u stanju %q bez poruke greške" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "stvorio korisnik %q\n" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "onemogućeno" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "greška: %v\n" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "greška: `` argument nije pružan i ne može se zaključiti" + +msgid "get which option?" +msgstr "nabaviti koju mogućnost?" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" +"svojstva sučelja mogu se samo čitati tijekom izvršavanja sučelja kvačila" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" +"svojstva sučelja mogu se samo postaviti tijekom izvršavanja pripremanja " +"kvačila" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "unutrašnja greška, prijavite: pokrenuto %q neuspjelo: %v\n" + +msgid "internal error: cannot find attrs task" +msgstr "unutrašnja greška: nemoguće pronaći svojstva zadatka" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" +"unutrašnja greška: nemoguće pronaći podatke priključka ili priključnice u " +"prikladnom zadatku" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "unutrašnja greška: nemoguće je dobaviti %s iz prikladnog zadatka" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "neispravno svojstvo: %q (potrebno je ključ=vrijednost)" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "neispravno podešavanje: %q (potrebno je ključ=vrijednost)" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "neispravan filter zaglavlja: %q (potrebno je ključ=vrijednost)" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "neispravan parametar: %q (potrebno je ključ=vrijednost)" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "neispravna vrijednost: %q (potreban je snap:naziv ili snap paket)" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" +"naziv ključa %q nije valjan; samo ASCII slova, brojevi i povlake su dopušteni" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "nedostaje snap-confine: pokušajte nadopuniti vaš snap paket" + +msgid "need the application to run as argument" +msgstr "potrebna je aplikacija za pokretanje argumenta" + +msgid "no changes found" +msgstr "nema pronađenih promjena" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "nema pronađenih sučelja" + +msgid "no matching snaps installed" +msgstr "nema odgovarajućih snap paketa instaliranih" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "nema odgovarajućih snap paketa zadanih" + +msgid "not a valid snap" +msgstr "nema valjanih snap paketa" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "privatno" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" +"zakazano ponovno pokretanje za nadopunu sustava - privremeno se prekida sa " +"'sudo shutdown -c'" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "postaviti koju mogućnost?" + +msgid "show detailed information about a snap" +msgstr "prikaži opširnije informacije o snap paketu" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "snap je besplatan" + +msgid "too many arguments for command" +msgstr "previše argumenata za naredbu" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "previše argumenata za kvačilo %q: %s" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "previše argumenata: %s" + +msgid "unavailable" +msgstr "nedostupno" + +#, c-format +msgid "unknown attribute %q" +msgstr "nepoznato svojstvo %q" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "nepoznata naredba %q, pogledajte \"snap --help\"" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "nepoznat priključak ili priključnica %q" + +#, c-format +msgid "unsupported shell %v" +msgstr "nepodržana ljuska %v" + +#~ msgid "" +#~ "\n" +#~ "The change command displays a summary of tasks associated to an individual " +#~ "change." +#~ msgstr "" +#~ "\n" +#~ "change - naredba prikazuje sažetak zadataka povezanih s pojedinačnom " +#~ "promjenom." + +#~ msgid "" +#~ "\n" +#~ "The find command queries the store for available packages.\n" +#~ msgstr "" +#~ "\n" +#~ "find - naredba pretražuje dostupne pakete u trgovini.\n" diff -Nru snapd-2.28.5/po/ia.po snapd-2.29.3/po/ia.po --- snapd-2.28.5/po/ia.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/ia.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Interlingua translation for snapd +# Copyright (c) 2017 Rosetta Contributors and Canonical Ltd 2017 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-01-23 00:42+0000\n" +"Last-Translator: karm \n" +"Language-Team: Interlingua \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "%s %s montate ex %s\n" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "%s jam installate\n" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/id.po snapd-2.29.3/po/id.po --- snapd-2.28.5/po/id.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/id.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Indonesian translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2016-10-18 01:38+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Indonesian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/it.po snapd-2.29.3/po/it.po --- snapd-2.28.5/po/it.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/it.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Italian translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2016-09-04 11:48+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Italian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/ja.po snapd-2.29.3/po/ja.po --- snapd-2.28.5/po/ja.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/ja.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Japanese translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2016-11-30 20:59+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Japanese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/lt.po snapd-2.29.3/po/lt.po --- snapd-2.28.5/po/lt.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/lt.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Lithuanian translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-09-23 15:49+0000\n" +"Last-Translator: Moo \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "%s %s prijungta iš %s\n" + +#, c-format +msgid "%s (delta)" +msgstr "%s (delta)" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "%s (žiūrėkite \"snap login --help\")" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "%s (pabandykite su sudo)" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "%s išjungta\n" + +#, c-format +msgid "%s enabled\n" +msgstr "%s įjungta\n" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "%s pašalinta\n" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "%s sugrąžinta į %s\n" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "%s%s %s iš \"%s\" įdiegta\n" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "%s%s %s įdiegta\n" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "Nutraukti laukiamą pakeitimą" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/ms.po snapd-2.29.3/po/ms.po --- snapd-2.28.5/po/ms.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/ms.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Malay translation for snapd +# Copyright (c) 2017 Rosetta Contributors and Canonical Ltd 2017 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-10-09 14:23+0000\n" +"Last-Translator: abuyop \n" +"Language-Team: Malay \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "%s (delta)" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/nb.po snapd-2.29.3/po/nb.po --- snapd-2.28.5/po/nb.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/nb.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1919 @@ +# Norwegian Bokmal translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2016-10-25 11:43+0000\n" +"Last-Translator: Åka Sikrom \n" +"Language-Team: Norwegian Bokmal \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "%s %s montert fra %s\n" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "%s (se «snap login --help»)" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "%s (prøv med sudo)" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "%s slått av\n" + +#, c-format +msgid "%s enabled\n" +msgstr "%s slått på\n" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "%s fjernet\n" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "%s tilbakestilt til %s\n" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "%s%s %s fra «%s» installert\n" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "%s%s %s installert\n" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "«--list» kan ikke brukes med modus- eller kanalvalg" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "«-r» kan bare brukes sammen med «--hook»" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr ":" + +msgid ":" +msgstr ":" + +msgid ":" +msgstr ":" + +msgid "Abort a pending change" +msgstr "Avbryt pågående endring" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "Legger til en forutsetning i systemet" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "Alias for «--dangerous» (UTGÅTT)" + +msgid "All snaps up to date." +msgstr "Alle snap-pakker er oppdatert." + +msgid "Alternative command to run" +msgstr "Alternativ kommando som skal kjøres" + +msgid "Always return document, even with single key" +msgstr "Vis alltid dokument, selv med enkeltknapp" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "E-postadresse til en bruker på login.ubuntu.com" + +msgid "Assertion file" +msgstr "Forutsetningsfil" + +msgid "Assertion type name" +msgstr "Forutsetningstypenavn" + +msgid "Authenticates on snapd and the store" +msgstr "Autentiserer mot snapd og butikk" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "Ugyldig kode. Prøv igjen: " + +msgid "Buys a snap" +msgstr "Kjøper en snap" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" +"Klassisk dimensjon er slått av på dette systemet.\n" +"Bruk «sudo snap install --devmode classic && sudo classic.create» for å slå " +"på." + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "Oppsettsverdi (nøkkel=verdi)" + +msgid "Confirm passphrase: " +msgstr "Bekreft passordfrase: " + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "Koble %s:%s til %s:%s" + +msgid "Connects a plug to a slot" +msgstr "Kobler en plugg til et spor" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "Begrens visning til bestemt snap eller snap:navn" + +msgid "Constrain listing to specific interfaces" +msgstr "Begrens visning til bestemt grensesnitt" + +msgid "Constrain listing to those matching header=value" +msgstr "Begrens visning til elementer som samsvarer med hode=verdi" + +#, c-format +msgid "Copy snap %q data" +msgstr "Kopier snap %q-data" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" +"Lag kryptografisk nøkkelpar som kan brukes til å signere forutsetninger." + +msgid "Create cryptographic key pair" +msgstr "Lag kryptografisk nøkkelpar" + +msgid "Create snap build assertion" +msgstr "Lag forutsetning for snap-build" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "Lag snap-build-forutsetning for valgt snap-fil." + +msgid "Creates a local system user" +msgstr "Lager lokal systembruker" + +msgid "Delete cryptographic key pair" +msgstr "Slett kryptografisk nøkkelpar" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "Sletter lokalt krytografisk nøkkelpar med valgt navn." + +#, c-format +msgid "Disable %q snap" +msgstr "Slå av %q snap" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "Slår av en snap i systemet" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "Forkast grensesnitt-tilkoblinger for snap %q (%s)" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "Koble %s:%s fra %s:%s" + +msgid "Disconnects a plug from a slot" +msgstr "Kobler en plugg fra et spor" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "Last ned snap %q%s fra kanal %q" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "Last ned valgt revisjon av en snap som du må ha utviklertilgang til" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "Slå på %q snap" + +msgid "Enables a snap in the system" +msgstr "Slår på en snap i systemet" + +msgid "Entering classic dimension" +msgstr "Går inn i klassisk dimensjon" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" +"Eksporter forutsetningskropp for offentlig nøkkel som kan importeres til " +"andre systemer." + +msgid "Export cryptographic public key" +msgstr "Eksporter kryptografisk offentlig nøkkel" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "Hent og kontroller forutsetninger for snap %q%s" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "Henter forutsetninger for %q\n" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "Henter snap %q\n" + +msgid "Filename of the snap you want to assert a build for" +msgstr "Filnavn på snap som du vil bygge for" + +msgid "Finds packages to install" +msgstr "Finner pakker som skal installeres" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" +"Formater offentlig nøkkelmateriell som forespørsel for en kontonøkkel til " +"denne konto-id-en" + +msgid "Generate device key" +msgstr "Lag enhetsnøkkel" + +msgid "Generate the manpage" +msgstr "Lag bruksanvisning («manpage»)" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "Grad viser snap-ens byggekvalitet (standard er «stable» - stabil)" + +msgid "Grant sudo access to the created user" +msgstr "Gi sudo-tilgang til opprettet bruker" + +msgid "Help" +msgstr "Hjelp" + +msgid "Hook to run" +msgstr "Krok som skal kjøres" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "ID\tStatus\tFramtoning\tKlar\tSammendrag\n" + +msgid "Identifier of the signer" +msgstr "Signatur-identifisering" + +msgid "Identifier of the snap package associated with the build" +msgstr "Identifisering av snap-pakke som er knyttet til bygget" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "Klargjør enhet" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "Installer %q snap" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "Installer %q snap fra %q kanal" + +#, c-format +msgid "Install %q snap from file" +msgstr "Installer %q snap fra fil" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "Installer %q snap fra fil %q" + +msgid "Install from the beta channel" +msgstr "Installer fra beta-kanal" + +msgid "Install from the candidate channel" +msgstr "Installer fra kandidat-kanal" + +msgid "Install from the edge channel" +msgstr "Installer fra edge-kanal" + +msgid "Install from the stable channel" +msgstr "Installer fra stable-kanal" + +#, c-format +msgid "Install snap %q" +msgstr "Installer snap %q" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "Installer %s" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "Installer valgt revisjon av en snap som du må ha utviklertilgang til" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" +"Installerer valgt snap-fil selv om det ikke finnes noen forhåndsgodkjente " +"signaturer for den. Dette betyr at snap-en ikke er bekreftet, og at den kan " +"være farlig (dette er vanlig ved bruk av «--devmode»)" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "Installerer en snap på systemet" + +msgid "Key of interest within the configuration" +msgstr "Interessenøkkel i oppsettet" + +msgid "List a change's tasks" +msgstr "Vis endringsoppgaver" + +msgid "List cryptographic keys" +msgstr "Vis kryptografiske nøkler" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" +"Vis kryptografiske nøkler som kan brukes til å signere forutsetninger." + +msgid "List installed snaps" +msgstr "Vis installerte snap-er" + +msgid "List system changes" +msgstr "Vis systemendringer" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "Vis systemgrensesnitt" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "Logg ut av butikk" + +msgid "Login successful" +msgstr "Du er logget inn" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "Gjør gjeldende revisjon av snap %q utilgjengelig" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "Gjør snap %q (%s) utilgjengelig for systemet" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "Gjør snap %q utilgjengelig for systemet" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "Gjør snap %q%s tilgjengelig for systemet" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "Monter snap %q%s" + +msgid "Name of key to create; defaults to 'default'" +msgstr "Navn på nøkkel som skal lages (standard er «default»)" + +msgid "Name of key to delete" +msgstr "Navn på nøkkel du vil slette" + +msgid "Name of key to export" +msgstr "Navn på nøkkel du vil eksportere" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" +"Navn på GnuPG-nøkkel du vil bruke (bruker navnet «default» som standard)" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "Navn på nøkkelen du vil bruke (hvis annet enn standardnøkkel)" + +msgid "Name\tSHA3-384" +msgstr "Navn\tSHA3-384" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "Navn\tVersjon\tUtvikler\tMerknader\tSammendrag" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "Navn\tVersjon\tRev\tUtvikler\tMerknader" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "Ingen snap-er er installert enda. Prøv «snap install hello-world»." + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "Skriv ut treff i JSON-format" + +msgid "Passphrase: " +msgstr "Passordfrase: " + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "Forbered snappy-bilde" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "Forbered snap %q (%s)" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "Forbered snap %q%s" + +msgid "Print the version and exit" +msgstr "Skriv ut versjon og avslutt" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "Oppdater %q snap" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "Oppdater %q snap fra %q kanal" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "Oppdater snap %q" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "Oppdater snap-er %s" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "Oppdater til valgt revisjon" + +msgid "Refreshes a snap in the system" +msgstr "Oppdaterer en snap på systemet" + +#, c-format +msgid "Remove %q snap" +msgstr "Fjern %q snap" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "Fjern snap-data %q (%s)" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "Bare fjern valgt revisjon" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "Fjern sikkerhetsprofil for snap %q (%s)" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "Fjern snap %q" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "Fjern snap %q (%s) fra systemet" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "Fjern snap-er %s" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "Fjerner en snap fra systemet" + +msgid "Request device serial" +msgstr "Be om enhets-serienummer" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "Tilbakestill %q snap" + +msgid "Reverts the given snap to the previous state" +msgstr "Tilbakestiller valgt snap til tidligere tilstand" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "Kjør skall i stedet for kommando (nyttig ved feilsøking)" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "Kjør valgt snap-kommando" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "Kjør valgt snap-kommando med korrekt begrensning og miljø" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "Søk etter private snap-er" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "Sett opp sikkerhetsprofiler for snap %q%s" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "Viser kjente forutsetninger av aktuell type" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "Signer forutsetning" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" +"Signer en forutsetning ved å bruke valgt nøkkel, og bruk hode-inndata fra " +"JSON-tilknytning via standard inndata. Bruk pseudo-hodet «body» for å ta med " +"innholdstekst for forutsetninga.\n" + +msgid "Slot\tPlug" +msgstr "Spor\tPlugg" + +msgid "Snap name" +msgstr "Snapnavn" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "Start tjenester for snap %q (%s)" + +#, c-format +msgid "Start snap %q%s services" +msgstr "Start tjenester for snap %q%s" + +msgid "Start snap services" +msgstr "Start snap-tjenester" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "Status\tFramtoning\tReady\tSummary\n" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "Stopp snap-tjenester %q (%s)" + +#, c-format +msgid "Stop snap %q services" +msgstr "Stopp snap-tjenester %q" + +msgid "Stop snap services" +msgstr "Stopp snap-tjenester" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "Tester en snap på systemet" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" +"Brukernavn/e-postadresse (login.ubuntu.com) som skal brukes til innlogging" + +msgid "The model assertion name" +msgstr "Modell-forutsetningsnavn" + +msgid "The output directory" +msgstr "Utdata-mappe" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "Snap som skal settes opp (f.eks. «hello-world»)" + +msgid "The snap whose conf is being requested" +msgstr "Snap som oppsett blir forespurt for" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "Denne kommandoen logger gjeldende bruker ut av butikken" + +msgid "Tool to interact with snaps" +msgstr "Interaksjonsverktøy for snap-er" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "Toveis-autentiseringskode: " + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "Bruk bestemt snap-revisjon ved kjørng av krok" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "Bruk denne kanalen i stedet for «stable» (stabil)" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "ADVARSEL: klarte ikke å slå på loggføring: %v\n" + +msgid "Waiting for server to restart" +msgstr "Venter på omstart av tjener" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "Feil igjen. Prøv én gang til: " + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "Ja, det gjør den." + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" +"Du må logge inn for å kjøpe programvare.Kjør «snap login» og prøv på nytt." + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" +"\n" +"Kommandoen «abort» prøver å avbryte en endring som fremdeles har pågående " +"oppgaver.\n" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" +"\n" +"Kommandoen «ack» prøver å legge til en forutsetning i systemets " +"forutsetningsdatabase.\n" +"\n" +"Forutsetninga kan også være en nyere revisjon av en eksisterende " +"forutsetning som blir erstattet.\n" +"\n" +"For at dette skal gå bra må forutsetninga være gyldig, signatur må være " +"bekreftet med en kjent offentlig nøkkel og forutsetninga må passe med " +"foregående forutsetning som ligger i systemdatabasen.\n" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" +"\n" +"Kommandoen «buy» kjøper en snap fra butikken.\n" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" +"\n" +"Kommandoen «changes» viser sammendrag av nylig utførte systemendringer." + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" +"\n" +"Kommandoen «create-user» lager en lokal systembruker med brukernavn og SSH-" +"nøkler\n" +"fra butikkontoen som er koblet med angitt e-postadresse.\n" +"\n" +"Gå til https://login.ubuntu.com hvis du vil lage ny konto.\n" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" +"\n" +"Kommandoen «disable» tar en snap ut av bruk. Binærfiler og tjenester\n" +"for snap-en blir gjort utilgjengelig. Alle data forblir tilgjengelig,\n" +"og snap-en kan enkelt tas i bruk igjen.\n" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" +"\n" +"Kommandoen «enable» tar en deaktivert snap i bruk igjen.\n" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" +"\n" +"Kommandoen «install» installerer valgt snap på systemet.\n" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" +"\n" +"Kommandoen «interfaces» viser tilgjengelige grensesnitt på systemet.\n" +"\n" +"Alle spor og plugger som brukes og tilbys av snap-er vises som standard.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Viser bare valgt spor eller plugg.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Viser spor som tilbys og plugger som brukes av valgt snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filtrerer utskrift til plugger og/eller spor som samsvarer med valgt[e] " +"gresensnitt/snap[-er].\n" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" +"\n" +"Kommandoen «known» viser kjente forutsetninger av valgt type.\n" +"Hvis du oppgir hode=verdi etter forutsetningstype, må forutsetninger også ha " +"valgte\n" +"hoder som samsvarer med valgte verdier.\n" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" +"\n" +"Kommandoen «list» viser sammendrag av snap-er som er installert på systemet." + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" +"\n" +"Kommandoen «login» autentiserer deg mot snapd og snap-butikken. \n" +"Akkreditiver lagres i fila «~/.snap/auth.json». Videre kommunikasjon med " +"snapd utføres\n" +"med angitte akkreditiver.\n" +"\n" +"Innlogging kan bare utføres av lokal bruker som er medlem av en av \n" +"gruppene sudo, admin og/eller wheel.\n" +"\n" +"Gå til https://login.ubuntu.com for å lage ny konto\n" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" +"\n" +"Kommandoen «refresh» oppdaterer valgt snap.\n" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" +"\n" +"Kommandoen «revert» tilbakestiller valgt snap til tilstanden den\n" +"hadde før forrige oppdatering. Denne handlinga tar i bruk forrige\n" +"snap-revisjon og bruker data som var tilknyttet denne revisjonen.\n" +"Dataendringer utført i seneste revisjon blir forkastet, unntatt data\n" +"som snap-en uttrykkelig velger å dele på tvers av revisjoner.\n" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" +"\n" +"\n" +"Hjemmemappe deles mellom dimensjonene «snappy» og «classic».\n" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "du må oppgi et snap-navn for å bruke modus- eller kanalvalg" + +msgid "a single snap name is needed to specify the revision" +msgstr "du må oppgi et snap-navn for å velge revisjon" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "kjøpt" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "ødelagt" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "klarte ikke å lage %q: %v" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "klarte ikke å lage forutsetningsfil: %v" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "klarte ikke å hente ut snap-navn fra lokal fil %q: %v" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "fant ikke program %q i %q" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "klaret ikke å hente data for %q: %v" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "klarte ikke å hente fullstendig filsti for %q: %v" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "fant ikke gjeldende bruker: %v" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "klarte ikke å merke oppstart som vellykket: %s" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "klarte ikke å åpne forutsetningsdatabase: %v" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "klarte ikke å lese forutsetningsinndata: %v" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "klarte ikke å signere forutsetning: %v" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "klarte ikke å bruke nøkkelen %q: %v" + +msgid "cannot use --hook and --command together" +msgstr "du kan ikke velge både «--hook» og «--command» samtidig" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "du kan ikke velge både «devmode» og «jailmode» samtidig" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "endring utført med status %q, uten feilmelding" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "slått av" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "feil: %v\n" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" +"intern programfeil. Rapporter følgende: \n" +"Kjøring av %q mislyktes: %v\n" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "attributt %q er ugyldig (du må bruke nøkkel=verdi)" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "oppsettsverdi %q er ugyldig (du må bruke nøkkel=verdi)" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "hodefilter %q er ugyldig (du må bruke nøkkel=verdi)" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "parameter %q er ugyldig (du må bruke nøkkel=verdi)" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "verdi %q er ugyldig (du må bruke «snap:navn» eller «snap»)" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" +"nøkkelnavn %q er ugyldig. Du kan bare bruke ASCII-bokstaver, sifre og " +"bindestreker" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "programmet må kjøres som argument" + +msgid "no changes found" +msgstr "fant ingen endringer" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "fant ingen grensesnitt" + +msgid "no matching snaps installed" +msgstr "fant ingen installerte snap-er som samsvarer" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "privat" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" +"omstart er planlagt for å oppdatere systemet. Avbryt dette midlertidig med " +"kommandoen «sudo shutdown -c»" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "gratis snap" + +msgid "too many arguments for command" +msgstr "du har brukt for mange argumenter" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "du har brukt for mange argumenter for krok %q: %s" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "du har brukt for mange argumenter: %s" + +msgid "unavailable" +msgstr "utilgjengelig" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "skallet %v støttes ikke" + +#~ msgid "" +#~ "\n" +#~ "The change command displays a summary of tasks associated to an individual " +#~ "change." +#~ msgstr "" +#~ "\n" +#~ "Kommandoen «change» viser sammendrag av oppgaver som er knyttet til en " +#~ "enkeltendring." + +#~ msgid "" +#~ "\n" +#~ "The find command queries the store for available packages.\n" +#~ msgstr "" +#~ "\n" +#~ "Kommandoen «find» ser etter tilgjengelige pakker i butikken.\n" diff -Nru snapd-2.28.5/po/oc.po snapd-2.29.3/po/oc.po --- snapd-2.28.5/po/oc.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/oc.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Occitan (post 1500) translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2016-12-04 08:58+0000\n" +"Last-Translator: Cédric VALMARY (Tot en òc) \n" +"Language-Team: Occitan (post 1500) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "%s %s montat dempuèi %s\n" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "%s (veire « snap login --help »)" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "%s desactivat\n" + +#, c-format +msgid "%s enabled\n" +msgstr "%s activat\n" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "%s suprimit\n" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "%s%s %s de « %s » installat\n" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "%s%s %s installat\n" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "desactivat" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "error : %v\n" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "privat" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "tròp d'arguments : %s" + +msgid "unavailable" +msgstr "indisponible" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/pt_BR.po snapd-2.29.3/po/pt_BR.po --- snapd-2.28.5/po/pt_BR.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/pt_BR.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Brazilian Portuguese translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2016-09-04 11:48+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Brazilian Portuguese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/pt.po snapd-2.29.3/po/pt.po --- snapd-2.28.5/po/pt.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/pt.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1804 @@ +# Portuguese translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-04-04 18:47+0000\n" +"Last-Translator: Ivo Xavier \n" +"Language-Team: Portuguese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "%s %s montado a partir de %s\n" + +#, c-format +msgid "%s (delta)" +msgstr "%s (delta)" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "%s (ver \"snap login --help\")" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "%s (tente com sudo)" + +#, c-format +msgid "%s already installed\n" +msgstr "%s já instalado\n" + +#, c-format +msgid "%s disabled\n" +msgstr "%s desativado\n" + +#, c-format +msgid "%s enabled\n" +msgstr "%s ativado\n" + +#, c-format +msgid "%s not installed\n" +msgstr "%s não instalado\n" + +#, c-format +msgid "%s removed\n" +msgstr "%s removido\n" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "%s revertido para %s\n" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "%s%s %s a partir de '%s' instalado\n" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "%s%s %s instalado\n" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "--list não aceita flags mode ou de canais" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "-r só pode ser usado com --hook" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr ":" + +msgid ":" +msgstr ":" + +msgid ":" +msgstr ":" + +msgid "Abort a pending change" +msgstr "Abortar uma alteração pendente" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "Adiciona uma asserção ao sistema" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "O mesmo que --perigoso (OBSOLETO)" + +msgid "All snaps up to date." +msgstr "Todos os snaps atualizados." + +msgid "Alternative command to run" +msgstr "Comando alternativo a executar" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "Um email de utilizador em login.ubuntu.com" + +msgid "Assertion file" +msgstr "Ficheiro de asserção" + +msgid "Assertion type name" +msgstr "Nome do tipo de asserção" + +msgid "Authenticates on snapd and the store" +msgstr "Autenticação no snapd e na loja" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "Compra um snap" + +msgid "Change ID" +msgstr "Alterar ID" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "Valor de configuração (chave=valor)" + +msgid "Confirm passphrase: " +msgstr "Confirme frase-passe: " + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "Ligue %s:%s a %s:%s" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "Copiar dados do snap %q" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "Cria um utilizador de sistema local" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "Desativar o snap %q" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "Desativa uma snap no sistema" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "Transferir o snap %q%s do canal %q" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "Endereço email: " + +#, c-format +msgid "Enable %q snap" +msgstr "Ativar o snap %q" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "Procurar pacotes para instalar" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "Gerar chave de dispositivo" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "Ajuda" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "Inicializar dispositivo" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "Instalar snap %q" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "Instalar snap %q do canal %q" + +#, c-format +msgid "Install %q snap from file" +msgstr "Instalar snap %q a partir de ficheiro" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "Instalar snap %q a partir do ficheiro %q" + +msgid "Install from the beta channel" +msgstr "Instalar a partir do canal beta" + +msgid "Install from the candidate channel" +msgstr "Instalar a partir do canal candidate" + +msgid "Install from the edge channel" +msgstr "Instalar a partir do canal edge" + +msgid "Install from the stable channel" +msgstr "Instalar a partir do canal stable" + +#, c-format +msgid "Install snap %q" +msgstr "Instalar snap %q" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "Instalar snaps %s" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "Instala um snap no sistema" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "Lista de snaps instalados" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "Fechar sessão da loja" + +msgid "Login successful" +msgstr "Autenticação com sucesso" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "Montar o snap %q%s" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "Nome da chave a eliminar" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "Nome\tSHA3-384" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "Nome\tVersão\tProgramador\tNotas\tResumo" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "Nome\tVersão\tRev\tProgramador\tNotas" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" +"Ainda sem snaps instalados. Experimente \"snap install hello-world\"." + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "Preparar uma imagem snappy" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "Preparar o snap %q (%s)" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "Preparar o snap %q%s" + +msgid "Print the version and exit" +msgstr "Imprimir a versão e sair" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "Atualizar o snap %q" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "Atualizar o snap %q do canal %q" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "Atualizar o snap %q" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "Atualizar os snaps %s" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "Atualizar para a dada revisão" + +msgid "Refreshes a snap in the system" +msgstr "Atualiza um snap no sistema" + +#, c-format +msgid "Remove %q snap" +msgstr "Remover o snap %q" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "Remover dados para o snap %q (%s)" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "Remover apenas a revisão dada" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "Remover o snap %q" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "Remover o snap %q (%s) do sistema" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "Remover os snaps %s" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "Remove um snap do sistema" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "Reverter o snap %q" + +msgid "Reverts the given snap to the previous state" +msgstr "Reverter o snap dado ao estado anterior" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "Procurar snaps privados" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "Mostrar todas as revisões" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "Mostrar detalhes da versão" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "Nome do snap" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "Iniciar serviços do snap %q (%s)" + +#, c-format +msgid "Start snap %q%s services" +msgstr "Iniciar serviços do snap %q%s" + +msgid "Start snap services" +msgstr "Iniciar serviços do snap" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "Estado\tGerado\tPronto\tResumo\n" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "Parar serviços do snap %q (%s)" + +#, c-format +msgid "Stop snap %q services" +msgstr "Parar serviços do snap %q" + +msgid "Stop snap services" +msgstr "Parar serviços snap" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "Testar um snap no sistema" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "O snap a configurar (ex: hello-world)" + +msgid "The snap whose conf is being requested" +msgstr "O snap cuja configuração está a ser pedida" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "Este comando termina a sessão do utilizador atual na loja" + +msgid "Tool to interact with snaps" +msgstr "Ferramentas para interagir com snaps" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "Transição de ubuntu-core para core" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "Código dois-fatores: " + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "Usar este canal em vez do 'stable'" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "À espera que o servidor reinicie" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/ru.po snapd-2.29.3/po/ru.po --- snapd-2.28.5/po/ru.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/ru.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1804 @@ +# Russian translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-02-04 19:27+0000\n" +"Last-Translator: Eugene Marshal \n" +"Language-Team: Russian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "%s (смотрите \"snap login --help\")" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "-r можно использовать только с --hook" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "Установить snap-пакет %q" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "Парольная фраза: " + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "Вывести версию и выйти" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "Выходной каталог" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "не удалось создать %q: %v" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "не удалось получить данные для %q: %v" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "не удалось получить полный путь для %q: %v" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "не удалось получить текущего пользователя: %v" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "нельзя использовать --hook и --command вместе" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "ошибка: %v\n" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" +"недопустимое имя ключа %q; разрешены только буквы ASCII, цифры и дефисы" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "слишком много аргументов для команды" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "слишком много аргументов: %s" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "неизвестная команда %q, см. \"snap --help\"" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/sv.po snapd-2.29.3/po/sv.po --- snapd-2.28.5/po/sv.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/sv.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Swedish translation for snapd +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2016-12-22 04:44+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Swedish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/tr.po snapd-2.29.3/po/tr.po --- snapd-2.28.5/po/tr.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/tr.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Turkish translation for snapd +# Copyright (c) 2017 Rosetta Contributors and Canonical Ltd 2017 +# This file is distributed under the same license as the snapd package. +# FIRST AUTHOR , 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: snapd\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2017-03-11 18:21+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Turkish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "%s zaten kurulu\n" + +#, c-format +msgid "%s disabled\n" +msgstr "%s devre dışı\n" + +#, c-format +msgid "%s enabled\n" +msgstr "%s devrede\n" + +#, c-format +msgid "%s not installed\n" +msgstr "%s kurulu değil\n" + +#, c-format +msgid "%s removed\n" +msgstr "%s kaldırıldı\n" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "%s%s %s kuruldu\n" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/po/ug.po snapd-2.29.3/po/ug.po --- snapd-2.28.5/po/ug.po 2016-04-14 20:59:39.000000000 +0000 +++ snapd-2.29.3/po/ug.po 2017-10-27 12:23:38.000000000 +0000 @@ -7,446 +7,1797 @@ msgstr "" "Project-Id-Version: snappy\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2015-10-15 15:53+0200\n" -"PO-Revision-Date: 2015-10-26 03:56+0000\n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2016-09-04 11:48+0000\n" "Last-Translator: Eltikin \n" "Language-Team: Uyghur \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2015-10-26 05:27+0000\n" -"X-Generator: Launchpad (build 17812)\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" -#. TRANSLATORS: the %s is a pkgname, the second a comma separated list of paths #, c-format -msgid "%s: %s\n" -msgstr "%s: %s\n" +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" -#. TRANSLATORS: the %s stand for "name", "version", "description" -#, c-format -msgid "%s\t%s\t%s (forks not shown: %d)\t" -msgstr "%s\t%s\t%s (forks not shown: %d)\t" +msgid "Watch a change in progress" +msgstr "" -#. TRANSLATORS: the first %s is a pkgname, the second %s is a path -#, c-format -msgid "'%s' is no longer allowed to access '%s'\n" +msgid "Wrong again. Once more: " msgstr "" -#. TRANSLATORS: the first %s is a pkgname, the second %s is a path #, c-format -msgid "'%s' is now allowed to access '%s'\n" +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." msgstr "" -#. TRANSLATORS: the first %s is a pkgname, the second %s is a path #, c-format -msgid "'%s' previously allowed access to '%s'. Skipping\n" +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." msgstr "" -#. TRANSLATORS: the %s is a pkgname +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" #, c-format -msgid "'%s:' is not allowed to access additional hardware\n" +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" msgstr "" -msgid "(deprecated) please use \"list\"" +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" msgstr "" -msgid "2fa code: " +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" msgstr "" msgid "" -"A concise summary of key attributes of the snappy system, such as the " -"release and channel.\n" "\n" -"The verbose output includes the specific version information for the factory " -"image, the running image and the image that will be run on reboot, together " -"with a list of the available channels for this image.\n" +"The ack command tries to add an assertion to the system assertion database.\n" "\n" -"Providing a package name will display information about a specific installed " -"package.\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" "\n" -"The verbose version of the info command for a package will also tell you the " -"available channels for that package, when it was installed for the first " -"time, disk space utilization, and in the case of frameworks, which apps are " -"able to use the framework." +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" msgstr "" -msgid "Activate a package" +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" msgstr "" msgid "" -"Activate a package that has previously been deactivated. If the package is " -"already activated, do nothing." +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" msgstr "" msgid "" -"Allows rollback of a snap to a previous installed version. Without any " -"arguments, the previous installed version is selected. It is also possible " -"to specify the version to rollback to as a additional argument.\n" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" msgstr "" -msgid "Assign a hardware device to a package" +msgid "" +"\n" +"The buy command buys a snap from the store.\n" msgstr "" -msgid "Assign hardware to a specific installed package" +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." msgstr "" -msgid "Builds a snap package" +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" msgstr "" -#. TRANSLATORS: the first %q is the file that can not be read and %v is the error message -#, c-format -msgid "Can't read hook file %q: %v" +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" msgstr "" msgid "" -"Configures a package. The configuration is a YAML file, provided in the " -"specified file which can be \"-\" for stdin. Output of the command is the " -"current configuration, so running this command with no input file provides a " -"snapshot of the app's current config." +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" msgstr "" -msgid "Creates a snap package and if available, runs the review scripts." +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" msgstr "" -msgid "Deactivate a package" +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" msgstr "" msgid "" -"Deactivate a package. If the package is already deactivated, do nothing." +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." msgstr "" -msgid "Display a summary of key attributes of the snappy system." +msgid "" +"\n" +"The install command installs the named snap in the system.\n" msgstr "" -msgid "Do not clean up old versions of the package." +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" msgstr "" -msgid "Ensures system is running with latest parts" +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" msgstr "" -msgid "First boot has already run" +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" msgstr "" -#. TRANSLATORS: the %s is a pkgname -#, c-format -msgid "Generated '%s' snap\n" +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." msgstr "" -msgid "Include information about packages from the snappy store" +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" msgstr "" -msgid "Install a snap package" +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" msgstr "" -msgid "Install snaps even if the signature can not be verified." +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" msgstr "" -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "Installing %s\n" -msgstr "ئورنىتىۋاتىدۇ %s\n" +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" -msgid "List active components installed on a snappy system" +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" msgstr "" -msgid "List assigned hardware device for a package" +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" msgstr "" -msgid "List assigned hardware for a specific installed package" +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" msgstr "" -msgid "Log into the store" +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" msgstr "" -msgid "Login successful" +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" msgstr "" -msgid "Name\tDate\tVersion\t" +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." msgstr "" -msgid "Name\tDate\tVersion\tDeveloper\t" +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" msgstr "" -msgid "Name\tVersion\tSummary\t" +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" msgstr "" -#. TRANSLATORS: the %s is a pkgname -#, c-format -msgid "No snap: '%s' found" +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" msgstr "" -msgid "Password: " -msgstr "ئىم: " +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" -msgid "Provide information about a specific installed package" +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" msgstr "" +#, c-format msgid "" -"Provides a list of all active components installed on a snappy system.\n" "\n" -"If requested, the command will find out if there are updates for any of the " -"components and indicate that by appending a * to the date. This will be " -"slower as it requires a round trip to the app store on the network.\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" "\n" -"The developer information refers to non-mainline versions of a package (much " -"like PPAs in deb-based Ubuntu). If the package is the primary version of " -"that package in Ubuntu then the developer info is not shown. This allows one " -"to identify packages which have custom, non-standard versions installed. As " -"a special case, the \"sideload\" developer refers to packages installed " -"manually on the system.\n" "\n" -"When a verbose listing is requested, information about the channel used is " -"displayed; which is one of alpha, beta, rc or stable, and all fields are " -"fully expanded too. In some cases, older (inactive) versions of snappy " -"packages will be installed, these will be shown in the verbose output and " -"the active version indicated with a * appended to the name of the component." +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" msgstr "" -msgid "Provides more detailed information" +msgid "bought" msgstr "" -msgid "Purge an installed package." +#. TRANSLATORS: if possible, a single short word +msgid "broken" msgstr "" -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "Purging %s\n" +msgid "cannot buy snap: %v" msgstr "" -msgid "Query and modify snappy services" +msgid "cannot buy snap: invalid characters in name" msgstr "" -msgid "Query and modify snappy services of locally-installed packages" +msgid "cannot buy snap: it has already been bought" msgstr "" -msgid "Query the store for available packages" +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" msgstr "" -msgid "Reboot if necessary to be on the latest running system." +#, c-format +msgid "cannot create assertions file: %v" msgstr "" -#. TRANSLATORS: the first %s is a pkgname the second a version +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message #, c-format -msgid "Reboot to use %s version %s." +msgid "cannot extract the snap-name from local file %q: %v" msgstr "" -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "Reboot to use the new %s." +msgid "cannot find app %q in %q" msgstr "" -#. TRANSLATORS: the %s shows a comma separated list -#. of package names #, c-format -msgid "Rebooting to satisfy updates for %s\n" +msgid "cannot find hook %q in %q" msgstr "" -msgid "Remove a snapp part" +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" msgstr "" -msgid "Remove all the data from the listed packages" +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" msgstr "" -msgid "" -"Remove all the data from the listed packages. Normally this is used for " -"packages that have been removed and attempting to purge data for an " -"installed package will result in an error. The --installed option overrides " -"that and enables the administrator to purge all data for an installed " -"package (effectively resetting the package completely)." +#, c-format +msgid "cannot get the current user: %s" msgstr "" -msgid "Remove hardware from a specific installed package" +#, c-format +msgid "cannot get the current user: %v" msgstr "" -#. TRANSLATORS: the %s is a pkgname #, c-format -msgid "Removing %s\n" -msgstr "ئۆچۈرۈۋاتىدۇ %s\n" +msgid "cannot mark boot successful: %s" +msgstr "" -msgid "Rollback to a previous version of a package" +#, c-format +msgid "cannot open the assertions database: %v" msgstr "" -msgid "Search for packages to install" +#, c-format +msgid "cannot read assertion input: %v" msgstr "" -msgid "Set configuration for a specific installed package" +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" msgstr "" -msgid "Set configuration for an installed package." +#, c-format +msgid "cannot resolve snap app %q: %v" msgstr "" -msgid "Set properties of system or package" +#, c-format +msgid "cannot sign assertion: %v" msgstr "" -msgid "" -"Set properties of system or package\n" -"\n" -"Supported properties are:\n" -" active=VERSION\n" -"\n" -"Example:\n" -" set hello-world active=1.0\n" +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" msgstr "" -#. TRANSLATORS: the first %s is a pkgname, the second %s is the new version +#. TRANSLATORS: %q is the key name, %v the error message #, c-format -msgid "Setting %s to version %s\n" +msgid "cannot use %q key: %v" msgstr "" -msgid "Show all available forks of a package" +msgid "cannot use --hook and --command together" msgstr "" -msgid "Show available updates (requires network)" +msgid "cannot use change ID and type together" msgstr "" -msgid "Show channel information and expand all fields" +msgid "cannot use devmode and jailmode flags together" msgstr "" -msgid "Snap\tService\tState" +#, c-format +msgid "cannot validate owner of file %s" msgstr "" -msgid "Specify an alternate output directory for the resulting package" +#, c-format +msgid "cannot write new Xauthority file at %s: %s" msgstr "" -msgid "The Package to install (name or path)" +#, c-format +msgid "change finished in status %q with no error message" msgstr "" +#, c-format msgid "" -"The \"versions\" command is no longer available.\n" -"\n" -"Please use the \"list\" command instead to see what is installed.\n" -"The \"list -u\" (or \"list --updates\") will show you the available updates\n" -"and \"list -v\" (or \"list --verbose\") will show all installed versions.\n" +"classic confinement requires snaps under /snap or symlink from /snap to %s" msgstr "" -msgid "The configuration for the given file" +#, c-format +msgid "created user %q\n" msgstr "" -msgid "The configuration for the given install" +#. TRANSLATORS: if possible, a single short word +msgid "disabled" msgstr "" -msgid "The hardware device path (e.g. /dev/ttyUSB0)" +msgid "email:" msgstr "" -msgid "The package to rollback " +msgid "enabled" msgstr "" -msgid "The version to rollback to" +#, c-format +msgid "error: %v\n" msgstr "" msgid "" -"This command adds access to a specific hardware device (e.g. /dev/ttyUSB0) " -"for an installed package." +"error: the `` argument was not provided and couldn't be inferred" msgstr "" -msgid "This command is no longer available, please use the \"list\" command" +msgid "get which option?" msgstr "" -msgid "This command list what hardware an installed package can access" +msgid "inactive" msgstr "" -msgid "This command logs the given username into the store" +msgid "" +"interface attributes can only be read during the execution of interface hooks" msgstr "" msgid "" -"This command removes access of a specific hardware device (e.g. " -"/dev/ttyUSB0) for an installed package." +"interface attributes can only be set during the execution of prepare hooks" msgstr "" -msgid "Unassign a hardware device to a package" +#, c-format +msgid "internal error, please report: running %q failed: %v\n" msgstr "" -msgid "Update all installed parts" +msgid "internal error: cannot find attrs task" msgstr "" -msgid "Use --show-all to see all available forks." +msgid "internal error: cannot find plug or slot data in the appropriate task" msgstr "" -msgid "Username for the login" +#, c-format +msgid "internal error: cannot get %s from appropriate task" msgstr "" -#. TRANSLATORS: the %s represents a list of installed appnames -#. (e.g. "apps: foo, bar, baz") -#, c-format -msgid "apps: %s\n" -msgstr "ئەپلەر %s\n" +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" -#. TRANSLATORS: the %s an architecture string #, c-format -msgid "architecture: %s\n" +msgid "invalid attribute: %q (want key=value)" msgstr "" -#. TRANSLATORS: the %s is a size #, c-format -msgid "binary-size: %v\n" +msgid "invalid configuration: %q (want key=value)" msgstr "" -#. TRANSLATORS: the %s is a channel name #, c-format -msgid "channel: %s\n" +msgid "invalid header filter: %q (want key=value)" msgstr "" -#. TRANSLATORS: the %s is a size #, c-format -msgid "data-size: %s\n" +msgid "invalid parameter: %q (want key=value)" msgstr "" -#. TRANSLATORS: the %s is a comma separated list of framework names #, c-format -msgid "frameworks: %s\n" +msgid "invalid value: %q (want snap:name or snap)" msgstr "" -#. TRANSLATORS: the %s is a date #, c-format -msgid "installed: %s\n" +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" msgstr "" -msgid "package name is required" +msgid "need the application to run as argument" msgstr "" -msgid "produces manpage" +msgid "no changes found" msgstr "" -#. TRANSLATORS: the %s release string #, c-format -msgid "release: %s\n" +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" msgstr "" msgid "" -"snappy autopilot triggered a reboot to boot into an up to date system -- " -"temprorarily disable the reboot by running 'sudo shutdown -c'" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" msgstr "" -#. TRANSLATORS: the first %s is the package name, the second is the service name; the %v is the error #, c-format -msgid "unable to disable %s's service %s: %v" +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" msgstr "" -#. TRANSLATORS: the first %s is the package name, the second is the service name; the %v is the error +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments #, c-format -msgid "unable to enable %s's service %s: %v" +msgid "too many arguments for hook %q: %s" msgstr "" +#. TRANSLATORS: the %s is the list of extra arguments #, c-format -msgid "unable to get logs: %v" +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" msgstr "" -#. TRANSLATORS: the first %s is the package name, the second is the service name; the %v is the error #, c-format -msgid "unable to start %s's service %s: %v" +msgid "unknown attribute %q" msgstr "" -#. TRANSLATORS: the first %s is the package name, the second is the service name; the %v is the error #, c-format -msgid "unable to stop %s's service %s: %v" +msgid "unknown command %q, see \"snap --help\"" msgstr "" -#. TRANSLATORS: the %s is a date #, c-format -msgid "updated: %s\n" +msgid "unknown plug or slot %q" msgstr "" -#. TRANSLATORS: the %s is a version string #, c-format -msgid "version: %s\n" +msgid "unsupported shell %v" msgstr "" diff -Nru snapd-2.28.5/po/zh_CN.po snapd-2.29.3/po/zh_CN.po --- snapd-2.28.5/po/zh_CN.po 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/po/zh_CN.po 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,1803 @@ +# Chinese (Simplified) translation for snappy +# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016 +# This file is distributed under the same license as the snappy package. +# FIRST AUTHOR , 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: snappy\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2017-10-14 00:51+0000\n" +"PO-Revision-Date: 2016-10-16 19:13+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Chinese (Simplified) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2017-10-18 08:37+0000\n" +"X-Generator: Launchpad (build 18476)\n" + +#, c-format +msgid "" +"%q does not contain an unpacked snap.\n" +"\n" +"Try \"snapcraft prime\" in your project directory, then \"snap try\" again." +msgstr "" + +#, c-format +msgid "%q switched to the %q channel\n" +msgstr "" + +#. TRANSLATORS: 1. snap name, 2. snap version (keep those together please). the 3rd %s is a path (where it's mounted from). +#, c-format +msgid "%s %s mounted from %s\n" +msgstr "" + +#, c-format +msgid "%s (delta)" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (see \"snap login --help\")" +msgstr "" + +#. TRANSLATORS: %s is an error message (e.g. “cannot yadda yadda: permission denied”) +#, c-format +msgid "%s (try with sudo)" +msgstr "" + +#, c-format +msgid "%s already installed\n" +msgstr "" + +#, c-format +msgid "%s disabled\n" +msgstr "" + +#, c-format +msgid "%s enabled\n" +msgstr "" + +#, c-format +msgid "%s not installed\n" +msgstr "" + +#, c-format +msgid "%s removed\n" +msgstr "" + +#, c-format +msgid "%s reverted to %s\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s from '%s' refreshed\n" +msgstr "" + +#, c-format +msgid "%s%s %s installed\n" +msgstr "" + +#, c-format +msgid "%s%s %s refreshed\n" +msgstr "" + +msgid "--list does not take mode nor channel flags" +msgstr "" + +msgid "--time does not take mode nor channel flags" +msgstr "" + +msgid "-r can only be used with --hook" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +#. TRANSLATORS: noun +#. TRANSLATORS: noun +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "
" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid "" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid ":" +msgstr "" + +msgid "Abort a pending change" +msgstr "" + +msgid "Added" +msgstr "" + +msgid "Adds an assertion to the system" +msgstr "" + +msgid "Alias for --dangerous (DEPRECATED)" +msgstr "" + +msgid "All snaps up to date." +msgstr "" + +msgid "Alternative command to run" +msgstr "" + +msgid "Always return document, even with single key" +msgstr "" + +#. TRANSLATORS: note users on login.ubuntu.com can have multiple email addresses +msgid "An email of a user on login.ubuntu.com" +msgstr "" + +msgid "Assertion file" +msgstr "" + +msgid "Assertion type name" +msgstr "" + +msgid "Authenticates on snapd and the store" +msgstr "" + +#, c-format +msgid "Auto-refresh %d snaps" +msgstr "" + +#, c-format +msgid "Auto-refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Auto-refresh snaps %s" +msgstr "" + +msgid "Bad code. Try again: " +msgstr "" + +msgid "Buys a snap" +msgstr "" + +msgid "Change ID" +msgstr "" + +msgid "Changes configuration options" +msgstr "" + +msgid "" +"Classic dimension disabled on this system.\n" +"Use \"sudo snap install --devmode classic && sudo classic.create\" to enable " +"it." +msgstr "" + +msgid "Command\tAlias\tNotes" +msgstr "" + +msgid "Configuration value (key=value)" +msgstr "" + +msgid "Confirm passphrase: " +msgstr "" + +#, c-format +msgid "Connect %s:%s to %s:%s" +msgstr "" + +msgid "Connects a plug to a slot" +msgstr "" + +msgid "Constrain listing to a specific snap or snap:name" +msgstr "" + +msgid "Constrain listing to specific interfaces" +msgstr "" + +msgid "Constrain listing to those matching header=value" +msgstr "" + +#, c-format +msgid "Copy snap %q data" +msgstr "" + +msgid "" +"Create a cryptographic key pair that can be used for signing assertions." +msgstr "" + +msgid "Create cryptographic key pair" +msgstr "" + +msgid "Create snap build assertion" +msgstr "" + +msgid "Create snap-build assertion for the provided snap file." +msgstr "" + +msgid "Creates a local system user" +msgstr "" + +msgid "Delete cryptographic key pair" +msgstr "" + +msgid "Delete the local cryptographic key pair with the given name." +msgstr "" + +#, c-format +msgid "Disable %q snap" +msgstr "" + +#, c-format +msgid "Disable aliases for snap %q" +msgstr "" + +#, c-format +msgid "Disable all aliases for snap %q" +msgstr "" + +msgid "Disables a snap in the system" +msgstr "" + +#, c-format +msgid "Discard interface connections for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Disconnect %s:%s from %s:%s" +msgstr "" + +msgid "Disconnects a plug from a slot" +msgstr "" + +msgid "Do not wait for the operation to finish but just print the change id." +msgstr "" + +#, c-format +msgid "Download snap %q%s from channel %q" +msgstr "" + +msgid "" +"Download the given revision of a snap, to which you must have developer " +"access" +msgstr "" + +msgid "Downloads the given snap" +msgstr "" + +msgid "Email address: " +msgstr "" + +#, c-format +msgid "Enable %q snap" +msgstr "" + +msgid "Enables a snap in the system" +msgstr "" + +msgid "Entering classic dimension" +msgstr "" + +msgid "" +"Export a public key assertion body that may be imported by other systems." +msgstr "" + +msgid "Export cryptographic public key" +msgstr "" + +#, c-format +msgid "Fetch and check assertions for snap %q%s" +msgstr "" + +#, c-format +msgid "Fetching assertions for %q\n" +msgstr "" + +#, c-format +msgid "Fetching snap %q\n" +msgstr "" + +msgid "Filename of the snap you want to assert a build for" +msgstr "" + +msgid "Finds packages to install" +msgstr "" + +msgid "Force adding the user, even if the device is already managed" +msgstr "" + +msgid "Force import on classic systems" +msgstr "" + +msgid "" +"Format public key material as a request for an account-key for this account-" +"id" +msgstr "" + +msgid "Generate device key" +msgstr "" + +msgid "Generate the manpage" +msgstr "" + +msgid "Grade states the build quality of the snap (defaults to 'stable')" +msgstr "" + +msgid "Grant sudo access to the created user" +msgstr "" + +msgid "Help" +msgstr "" + +msgid "Hook to run" +msgstr "" + +msgid "ID\tStatus\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Identifier of the signer" +msgstr "" + +msgid "Identifier of the snap package associated with the build" +msgstr "" + +msgid "Ignore validation by other snaps blocking the refresh" +msgstr "" + +#, c-format +msgid "" +"In order to buy %q, you need to agree to the latest terms and conditions. " +"Please visit https://my.ubuntu.com/payment/edit to do this.\n" +"\n" +"Once completed, return here and run 'snap buy %s' again." +msgstr "" + +msgid "Include a verbose list of a snap's notes (otherwise, summarise notes)" +msgstr "" + +msgid "Include unused interfaces" +msgstr "" + +msgid "Initialize device" +msgstr "" + +msgid "Inspects devices for actionable information" +msgstr "" + +#, c-format +msgid "Install %q snap" +msgstr "" + +#, c-format +msgid "Install %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Install %q snap from file" +msgstr "" + +#, c-format +msgid "Install %q snap from file %q" +msgstr "" + +msgid "Install from the beta channel" +msgstr "" + +msgid "Install from the candidate channel" +msgstr "" + +msgid "Install from the edge channel" +msgstr "" + +msgid "Install from the stable channel" +msgstr "" + +#, c-format +msgid "Install snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Install snaps %s" +msgstr "" + +msgid "" +"Install the given revision of a snap, to which you must have developer access" +msgstr "" + +msgid "" +"Install the given snap file even if there are no pre-acknowledged signatures " +"for it, meaning it was not verified and could be dangerous (--devmode " +"implies this)" +msgstr "" + +msgid "Install the given snap without enabling its automatic aliases" +msgstr "" + +msgid "Installs a snap to the system" +msgstr "" + +msgid "Key of interest within the configuration" +msgstr "" + +msgid "List a change's tasks" +msgstr "" + +msgid "List cryptographic keys" +msgstr "" + +msgid "List cryptographic keys that can be used for signing assertions." +msgstr "" + +msgid "List installed snaps" +msgstr "" + +msgid "List system changes" +msgstr "" + +msgid "Lists aliases in the system" +msgstr "" + +msgid "Lists interfaces in the system" +msgstr "" + +msgid "Lists snap interfaces" +msgstr "" + +msgid "Log out of the store" +msgstr "" + +msgid "Login successful" +msgstr "" + +#, c-format +msgid "Make current revision for snap %q unavailable" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) available to the system" +msgstr "" + +#, c-format +msgid "Make snap %q (%s) unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q unavailable to the system" +msgstr "" + +#, c-format +msgid "Make snap %q%s available to the system" +msgstr "" + +msgid "Mark system seeded" +msgstr "" + +#, c-format +msgid "Mount snap %q%s" +msgstr "" + +msgid "Name of key to create; defaults to 'default'" +msgstr "" + +msgid "Name of key to delete" +msgstr "" + +msgid "Name of key to export" +msgstr "" + +msgid "Name of the GnuPG key to use (defaults to 'default' as key name)" +msgstr "" + +msgid "Name of the key to use, otherwise use the default key" +msgstr "" + +msgid "Name\tSHA3-384" +msgstr "" + +msgid "Name\tSummary" +msgstr "" + +msgid "Name\tVersion\tDeveloper\tNotes\tSummary" +msgstr "" + +msgid "Name\tVersion\tRev\tDeveloper\tNotes" +msgstr "" + +msgid "No snaps are installed yet. Try \"snap install hello-world\"." +msgstr "" + +msgid "No snaps to auto-refresh found" +msgstr "" + +msgid "Output results in JSON format" +msgstr "" + +msgid "Passphrase: " +msgstr "" + +#, c-format +msgid "Password of %q: " +msgstr "" + +#. TRANSLATORS: %q, %q and %s are the snap name, developer, and price. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Please re-enter your Ubuntu One password to purchase %q from %q\n" +"for %s. Press ctrl-c to cancel." +msgstr "" + +#, c-format +msgid "Prefer aliases for snap %q" +msgstr "" + +msgid "Prefer aliases from a snap and disable conflicts" +msgstr "" + +#, c-format +msgid "Prefer aliases of snap %q" +msgstr "" + +msgid "Prepare a snappy image" +msgstr "" + +#, c-format +msgid "Prepare snap %q (%s)" +msgstr "" + +#, c-format +msgid "Prepare snap %q%s" +msgstr "" + +msgid "Print the version and exit" +msgstr "" + +msgid "Prints configuration options" +msgstr "" + +msgid "Prints the confinement mode the system operates in" +msgstr "" + +msgid "Prints the email the user is logged in with." +msgstr "" + +msgid "Prints whether system is managed" +msgstr "" + +#, c-format +msgid "Prune automatic aliases for snap %q" +msgstr "" + +msgid "Put snap in classic mode and disable security confinement" +msgstr "" + +msgid "Put snap in development mode and disable security confinement" +msgstr "" + +msgid "Put snap in enforced confinement mode" +msgstr "" + +msgid "Query the status of services" +msgstr "" + +#, c-format +msgid "Refresh %q snap" +msgstr "" + +#, c-format +msgid "Refresh %q snap from %q channel" +msgstr "" + +#, c-format +msgid "Refresh aliases for snap %q" +msgstr "" + +msgid "Refresh all snaps: no updates" +msgstr "" + +#, c-format +msgid "Refresh snap %q" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Refresh snaps %s: no updates" +msgstr "" + +msgid "Refresh to the given revision" +msgstr "" + +msgid "Refreshes a snap in the system" +msgstr "" + +#, c-format +msgid "Remove %q snap" +msgstr "" + +#, c-format +msgid "Remove aliases for snap %q" +msgstr "" + +#, c-format +msgid "Remove data for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove manual alias %q for snap %q" +msgstr "" + +msgid "Remove only the given revision" +msgstr "" + +#, c-format +msgid "Remove security profile for snap %q (%s)" +msgstr "" + +#, c-format +msgid "Remove security profiles of snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q" +msgstr "" + +#, c-format +msgid "Remove snap %q (%s) from the system" +msgstr "" + +#. TRANSLATORS: the %s is a comma-separated list of quoted snap names +#, c-format +msgid "Remove snaps %s" +msgstr "" + +msgid "Removed" +msgstr "" + +msgid "Removes a snap from the system" +msgstr "" + +msgid "Request device serial" +msgstr "" + +msgid "Restart services" +msgstr "" + +msgid "Restarted.\n" +msgstr "" + +msgid "Restrict the search to a given section" +msgstr "" + +msgid "Retrieve logs of services" +msgstr "" + +#, c-format +msgid "Revert %q snap" +msgstr "" + +msgid "Reverts the given snap to the previous state" +msgstr "" + +msgid "Run a shell instead of the command (useful for debugging)" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap" +msgstr "" + +#, c-format +msgid "Run configure hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run hook %s of snap %q" +msgstr "" + +#, c-format +msgid "Run install hook of %q snap if present" +msgstr "" + +#, c-format +msgid "Run post-refresh hook of %q snap if present" +msgstr "" + +msgid "Run prepare-device hook" +msgstr "" + +#, c-format +msgid "Run remove hook of %q snap if present" +msgstr "" + +msgid "Run the given snap command" +msgstr "" + +msgid "Run the given snap command with the right confinement and environment" +msgstr "" + +msgid "Runs debug commands" +msgstr "" + +msgid "Search private snaps" +msgstr "" + +msgid "" +"Select last change of given type (install, refresh, remove, try, auto-" +"refresh etc.)" +msgstr "" + +#, c-format +msgid "Set automatic aliases for snap %q" +msgstr "" + +msgid "Sets up a manual alias" +msgstr "" + +#, c-format +msgid "Setup alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup manual alias %q => %q for snap %q" +msgstr "" + +#, c-format +msgid "Setup snap %q (%s) security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q aliases" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles" +msgstr "" + +#, c-format +msgid "Setup snap %q%s security profiles (phase 2)" +msgstr "" + +msgid "Show all revisions" +msgstr "" + +msgid "Show auto refresh information but do not perform a refresh" +msgstr "" + +msgid "Show available snaps for refresh but do not perform a refresh" +msgstr "" + +msgid "Show details of a specific interface" +msgstr "" + +msgid "Show interface attributes" +msgstr "" + +msgid "Shows known assertions of the provided type" +msgstr "" + +msgid "Shows version details" +msgstr "" + +msgid "Sign an assertion" +msgstr "" + +msgid "" +"Sign an assertion using the specified key, using the input for headers from " +"a JSON mapping provided through stdin, the body of the assertion can be " +"specified through a \"body\" pseudo-header.\n" +msgstr "" + +msgid "Slot\tPlug" +msgstr "" + +msgid "Snap name" +msgstr "" + +msgid "Snap\tService\tStartup\tCurrent" +msgstr "" + +msgid "" +"Sorry, your payment method has been declined by the issuer. Please review " +"your\n" +"payment details at https://my.ubuntu.com/payment/edit and try again." +msgstr "" + +msgid "Start services" +msgstr "" + +#, c-format +msgid "Start snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Start snap %q%s services" +msgstr "" + +msgid "Start snap services" +msgstr "" + +msgid "Start the userd service" +msgstr "" + +msgid "Started.\n" +msgstr "" + +msgid "Status\tSpawn\tReady\tSummary\n" +msgstr "" + +msgid "Stop services" +msgstr "" + +#, c-format +msgid "Stop snap %q (%s) services" +msgstr "" + +#, c-format +msgid "Stop snap %q services" +msgstr "" + +msgid "Stop snap services" +msgstr "" + +msgid "Stopped.\n" +msgstr "" + +msgid "Strict typing with nulls and quoted strings" +msgstr "" + +#, c-format +msgid "Switch %q snap to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q from %s to %s" +msgstr "" + +#, c-format +msgid "Switch snap %q to %s" +msgstr "" + +msgid "Switches snap to a different channel" +msgstr "" + +msgid "Temporarily mount device before inspecting" +msgstr "" + +msgid "Tests a snap in the system" +msgstr "" + +#. TRANSLATORS: %q and %s are the same snap name. Please wrap the translation at 80 characters. +#, c-format +msgid "" +"Thanks for purchasing %q. You may now install it on any of your devices\n" +"with 'snap install %s'." +msgstr "" + +msgid "" +"The get command prints configuration and interface connection settings." +msgstr "" + +msgid "The login.ubuntu.com email to login as" +msgstr "" + +msgid "The model assertion name" +msgstr "" + +msgid "The output directory" +msgstr "" + +#. TRANSLATORS: the %q is the (quoted) query the user entered +#, c-format +msgid "The search %q returned 0 snaps\n" +msgstr "" + +msgid "The snap to configure (e.g. hello-world)" +msgstr "" + +msgid "The snap whose conf is being requested" +msgstr "" + +msgid "The userd command starts the snap user session service." +msgstr "" + +msgid "This command logs the current user out of the store" +msgstr "" + +msgid "Tool to interact with snaps" +msgstr "" + +#, c-format +msgid "Transition security profiles from %q to %q" +msgstr "" + +msgid "Transition ubuntu-core to core" +msgstr "" + +#, c-format +msgid "Try %q snap from %s" +msgstr "" + +msgid "Two-factor code: " +msgstr "" + +msgid "Unalias a manual alias or an entire snap" +msgstr "" + +msgid "Use a specific snap revision when running hook" +msgstr "" + +msgid "Use known assertions for user creation" +msgstr "" + +msgid "Use this channel instead of stable" +msgstr "" + +#, c-format +msgid "WARNING: failed to activate logging: %v\n" +msgstr "" + +msgid "Waiting for server to restart" +msgstr "" + +msgid "Watch a change in progress" +msgstr "" + +msgid "Wrong again. Once more: " +msgstr "" + +#, c-format +msgid "Xauthority file isn't owned by the current user %s" +msgstr "" + +msgid "Yes, yes it does." +msgstr "" + +msgid "" +"You need to be logged in to purchase software. Please run 'snap login' and " +"try again." +msgstr "" + +#, c-format +msgid "" +"You need to have a payment method associated with your account in order to " +"buy a snap, please visit https://my.ubuntu.com/payment/edit to add one.\n" +"\n" +"Once you’ve added your payment details, you just need to run 'snap buy %s' " +"again." +msgstr "" + +#. TRANSLATORS: the %s is the argument given by the user to "snap changes" +#, c-format +msgid "\"snap changes\" command expects a snap name, try: \"snap tasks %s\"" +msgstr "" + +msgid "" +"\n" +"Install, configure, refresh and remove snap packages. Snaps are\n" +"'universal' packages that work across many different Linux systems,\n" +"enabling secure distribution of the latest apps and utilities for\n" +"cloud, servers, desktops and the internet of things.\n" +"\n" +"This is the CLI for snapd, a background service that takes care of\n" +"snaps on the system. Start with 'snap list' to see installed snaps.\n" +msgstr "" + +msgid "" +"\n" +"The abort command attempts to abort a change that still has pending tasks.\n" +msgstr "" + +msgid "" +"\n" +"The ack command tries to add an assertion to the system assertion database.\n" +"\n" +"The assertion may also be a newer revision of a preexisting assertion that " +"it\n" +"will replace.\n" +"\n" +"To succeed the assertion must be valid, its signature verified with a known\n" +"public key and the assertion consistent with and its prerequisite in the\n" +"database.\n" +msgstr "" + +msgid "" +"\n" +"The alias command aliases the given snap application to the given alias.\n" +"\n" +"Once this manual alias is setup the respective application command can be " +"invoked just using the alias.\n" +msgstr "" + +msgid "" +"\n" +"The aliases command lists all aliases available in the system and their " +"status.\n" +"\n" +"$ snap aliases \n" +"\n" +"Lists only the aliases defined by the specified snap.\n" +"\n" +"An alias noted as undefined means it was explicitly enabled or disabled but " +"is\n" +"not defined in the current revision of the snap; possibly temporarely (e.g\n" +"because of a revert), if not this can be cleared with snap alias --reset.\n" +msgstr "" + +msgid "" +"\n" +"The auto-import command searches available mounted devices looking for\n" +"assertions that are signed by trusted authorities, and potentially\n" +"performs system changes based on them.\n" +"\n" +"If one or more device paths are provided via --mount, these are temporariy\n" +"mounted to be inspected as well. Even in that case the command will still\n" +"consider all available mounted devices for inspection.\n" +"\n" +"Imported assertions must be made available in the auto-import.assert file\n" +"in the root of the filesystem.\n" +msgstr "" + +msgid "" +"\n" +"The buy command buys a snap from the store.\n" +msgstr "" + +msgid "" +"\n" +"The changes command displays a summary of the recent system changes " +"performed." +msgstr "" + +msgid "" +"\n" +"The confinement command will print the confinement mode (strict, partial or " +"none)\n" +"the system operates in.\n" +msgstr "" + +msgid "" +"\n" +"The connect command connects a plug to a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap connect : :\n" +"\n" +"Connects the provided plug to the given slot.\n" +"\n" +"$ snap connect : \n" +"\n" +"Connects the specific plug to the only slot in the provided snap that " +"matches\n" +"the connected interface. If more than one potential slot exists, the " +"command\n" +"fails.\n" +"\n" +"$ snap connect :\n" +"\n" +"Connects the provided plug to the slot in the core snap with a name " +"matching\n" +"the plug name.\n" +msgstr "" + +msgid "" +"\n" +"The create-user command creates a local system user with the username and " +"SSH\n" +"keys registered on the store account identified by the provided email " +"address.\n" +"\n" +"An account can be setup at https://login.ubuntu.com.\n" +msgstr "" + +msgid "" +"\n" +"The debug command contains a selection of additional sub-commands.\n" +"\n" +"Debug commands can be removed without notice and may not work on\n" +"non-development systems.\n" +msgstr "" + +msgid "" +"\n" +"The disable command disables a snap. The binaries and services of the\n" +"snap will no longer be available. But all the data is still available\n" +"and the snap can easily be enabled again.\n" +msgstr "" + +msgid "" +"\n" +"The disconnect command disconnects a plug from a slot.\n" +"It may be called in the following ways:\n" +"\n" +"$ snap disconnect : :\n" +"\n" +"Disconnects the specific plug from the specific slot.\n" +"\n" +"$ snap disconnect :\n" +"\n" +"Disconnects everything from the provided plug or slot.\n" +"The snap name may be omitted for the core snap.\n" +msgstr "" + +msgid "" +"\n" +"The download command downloads the given snap and its supporting assertions\n" +"to the current directory under .snap and .assert file extensions, " +"respectively.\n" +msgstr "" + +msgid "" +"\n" +"The enable command enables a snap that was previously disabled.\n" +msgstr "" + +msgid "" +"\n" +"The find command queries the store for available packages in the stable " +"channel.\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the current snap.\n" +"\n" +" $ snapctl get username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snapctl get username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snapctl get author.name\n" +" frank\n" +"\n" +"Values of interface connection settings may be printed with:\n" +"\n" +" $ snapctl get :myplug usb-vendor\n" +" $ snapctl get :myslot path\n" +"\n" +"This will return the named setting from the local interface endpoint, " +"whether a plug\n" +"or a slot. Returning the setting from the connected snap's endpoint is also " +"possible\n" +"by explicitly requesting that via the --plug and --slot command line " +"options:\n" +"\n" +" $ snapctl get :myplug --slot usb-vendor\n" +"\n" +"This requests the \"usb-vendor\" setting from the slot that is connected to " +"\"myplug\".\n" +msgstr "" + +msgid "" +"\n" +"The get command prints configuration options for the provided snap.\n" +"\n" +" $ snap get snap-name username\n" +" frank\n" +"\n" +"If multiple option names are provided, a document is returned:\n" +"\n" +" $ snap get snap-name username password\n" +" {\n" +" \"username\": \"frank\",\n" +" \"password\": \"...\"\n" +" }\n" +"\n" +"Nested values may be retrieved via a dotted path:\n" +"\n" +" $ snap get snap-name author.name\n" +" frank\n" +msgstr "" + +msgid "" +"\n" +"The help command shows helpful information. Unlike this. ;-)\n" +msgstr "" + +msgid "" +"\n" +"The info command shows detailed information about a snap, be it by name or " +"by path." +msgstr "" + +msgid "" +"\n" +"The install command installs the named snap in the system.\n" +msgstr "" + +msgid "" +"\n" +"The interface command shows details of snap interfaces.\n" +"\n" +"If no interface name is provided, a list of interface names with at least\n" +"one connection is shown, or a list of all interfaces if --all is provided.\n" +msgstr "" + +msgid "" +"\n" +"The interfaces command lists interfaces available in the system.\n" +"\n" +"By default all slots and plugs, used and offered by all snaps, are " +"displayed.\n" +" \n" +"$ snap interfaces :\n" +"\n" +"Lists only the specified slot or plug.\n" +"\n" +"$ snap interfaces \n" +"\n" +"Lists the slots offered and plugs used by the specified snap.\n" +"\n" +"$ snap interfaces -i= []\n" +"\n" +"Filters the complete output so only plugs and/or slots matching the provided " +"details are listed.\n" +msgstr "" + +msgid "" +"\n" +"The known command shows known assertions of the provided type.\n" +"If header=value pairs are provided after the assertion type, the assertions\n" +"shown must also have the specified headers matching the provided values.\n" +msgstr "" + +msgid "" +"\n" +"The list command displays a summary of snaps installed in the current system." +msgstr "" + +msgid "" +"\n" +"The login command authenticates on snapd and the snap store and saves " +"credentials\n" +"into the ~/.snap/auth.json file. Further communication with snapd will then " +"be made\n" +"using those credentials.\n" +"\n" +"Login only works for local users in the sudo, admin or wheel groups.\n" +"\n" +"An account can be setup at https://login.ubuntu.com\n" +msgstr "" + +msgid "" +"\n" +"The managed command will print true or false informing whether\n" +"snapd has registered users.\n" +msgstr "" + +msgid "" +"\n" +"The prefer command enables all aliases of the given snap in preference\n" +"to conflicting aliases of other snaps whose aliases will be disabled\n" +"(removed for manual ones).\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"The publisher of snap %q has indicated that they do not consider this " +"revision\n" +"to be of production quality and that it is only meant for development or " +"testing\n" +"at this point. As a consequence this snap will not refresh automatically and " +"may\n" +"perform arbitrary system changes outside of the security sandbox snaps are\n" +"generally confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"devmode;\n" +"if instead you want to install the snap forcing it into strict confinement\n" +"repeat the command including --jailmode." +msgstr "" + +msgid "" +"\n" +"The refresh command refreshes (updates) the named snap.\n" +msgstr "" + +msgid "" +"\n" +"The remove command removes the named snap from the system.\n" +"\n" +"By default all the snap revisions are removed, including their data and the " +"common\n" +"data directory. When a --revision option is passed only the specified " +"revision is\n" +"removed.\n" +msgstr "" + +msgid "" +"\n" +"The revert command reverts the given snap to its state before\n" +"the latest refresh. This will reactivate the previous snap revision,\n" +"and will use the original data that was associated with that revision,\n" +"discarding any data changes that were done by the latest revision. As\n" +"an exception, data which the snap explicitly chooses to share across\n" +"revisions is not touched by the revert process.\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snap set snap-name username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the\n" +"snap's configuration hook returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snap set author.name=frank\n" +msgstr "" + +msgid "" +"\n" +"The set command changes the provided configuration options as requested.\n" +"\n" +" $ snapctl set username=frank password=$PASSWORD\n" +"\n" +"All configuration changes are persisted at once, and only after the hook\n" +"returns successfully.\n" +"\n" +"Nested values may be modified via a dotted path:\n" +"\n" +" $ snapctl set author.name=frank\n" +"\n" +"Plug and slot attributes may be set in the respective prepare and connect " +"hooks by\n" +"naming the respective plug or slot:\n" +"\n" +" $ snapctl set :myplug path=/dev/ttyS0\n" +msgstr "" + +msgid "" +"\n" +"The switch command switches the given snap to a different channel without\n" +"doing a refresh.\n" +msgstr "" + +msgid "" +"\n" +"The tasks command displays a summary of tasks associated to an individual " +"change." +msgstr "" + +msgid "" +"\n" +"The try command installs an unpacked snap into the system for testing " +"purposes.\n" +"The unpacked snap content continues to be used even after installation, so\n" +"non-metadata changes there go live instantly. Metadata changes such as " +"those\n" +"performed in snap.yaml will require reinstallation to go live.\n" +"\n" +"If snap-dir argument is omitted, the try command will attempt to infer it " +"if\n" +"either snapcraft.yaml file and prime directory or meta/snap.yaml file can " +"be\n" +"found relative to current working directory.\n" +msgstr "" + +msgid "" +"\n" +"The unalias command tears down a manual alias when given one or disables all " +"aliases of a snap, removing also all manual ones, when given a snap name.\n" +msgstr "" + +msgid "" +"\n" +"The version command displays the versions of the running client, server,\n" +"and operating system.\n" +msgstr "" + +msgid "" +"\n" +"The watch command waits for the given change-id to finish and shows " +"progress\n" +"(if available).\n" +msgstr "" + +msgid "" +"\n" +"The whoami command prints the email the user is logged in with.\n" +msgstr "" + +#, c-format +msgid "" +"\n" +"This revision of snap %q was published using classic confinement and thus " +"may\n" +"perform arbitrary system changes outside of the security sandbox that snaps " +"are\n" +"usually confined to, which may put your system at risk.\n" +"\n" +"If you understand and want to proceed repeat the command including --" +"classic.\n" +msgstr "" + +msgid "" +"\n" +"\n" +"The home directory is shared between snappy and the classic dimension.\n" +"Run \"exit\" to leave the classic shell.\n" +msgstr "" + +msgid "a single snap name is needed to specify mode or channel flags" +msgstr "" + +msgid "a single snap name is needed to specify the revision" +msgstr "" + +msgid "a single snap name must be specified when ignoring validation" +msgstr "" + +msgid "active" +msgstr "" + +msgid "bought" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "broken" +msgstr "" + +#, c-format +msgid "cannot buy snap: %v" +msgstr "" + +msgid "cannot buy snap: invalid characters in name" +msgstr "" + +msgid "cannot buy snap: it has already been bought" +msgstr "" + +#. TRANSLATORS: %q is the directory whose creation failed, %v the error message +#, c-format +msgid "cannot create %q: %v" +msgstr "" + +#, c-format +msgid "cannot create assertions file: %v" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v gets the resulting error message +#, c-format +msgid "cannot extract the snap-name from local file %q: %v" +msgstr "" + +#, c-format +msgid "cannot find app %q in %q" +msgstr "" + +#, c-format +msgid "cannot find hook %q in %q" +msgstr "" + +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#. TRANSLATORS: %q gets the snap name, %v the list of things found when trying to list it +#, c-format +msgid "cannot get data for %q: %v" +msgstr "" + +#. TRANSLATORS: %q gets what the user entered, %v gets the resulting error message +#, c-format +msgid "cannot get full path for %q: %v" +msgstr "" + +#, c-format +msgid "cannot get the current user: %s" +msgstr "" + +#, c-format +msgid "cannot get the current user: %v" +msgstr "" + +#, c-format +msgid "cannot mark boot successful: %s" +msgstr "" + +#, c-format +msgid "cannot open the assertions database: %v" +msgstr "" + +#, c-format +msgid "cannot read assertion input: %v" +msgstr "" + +#. TRANSLATORS: %v the error message +#, c-format +msgid "cannot read symlink: %v" +msgstr "" + +#, c-format +msgid "cannot resolve snap app %q: %v" +msgstr "" + +#, c-format +msgid "cannot sign assertion: %v" +msgstr "" + +#, c-format +msgid "cannot update the 'current' symlink of %q: %v" +msgstr "" + +#. TRANSLATORS: %q is the key name, %v the error message +#, c-format +msgid "cannot use %q key: %v" +msgstr "" + +msgid "cannot use --hook and --command together" +msgstr "" + +msgid "cannot use change ID and type together" +msgstr "" + +msgid "cannot use devmode and jailmode flags together" +msgstr "" + +#, c-format +msgid "cannot validate owner of file %s" +msgstr "" + +#, c-format +msgid "cannot write new Xauthority file at %s: %s" +msgstr "" + +#, c-format +msgid "change finished in status %q with no error message" +msgstr "" + +#, c-format +msgid "" +"classic confinement requires snaps under /snap or symlink from /snap to %s" +msgstr "" + +#, c-format +msgid "created user %q\n" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "disabled" +msgstr "" + +msgid "email:" +msgstr "" + +msgid "enabled" +msgstr "" + +#, c-format +msgid "error: %v\n" +msgstr "" + +msgid "" +"error: the `` argument was not provided and couldn't be inferred" +msgstr "" + +msgid "get which option?" +msgstr "" + +msgid "inactive" +msgstr "" + +msgid "" +"interface attributes can only be read during the execution of interface hooks" +msgstr "" + +msgid "" +"interface attributes can only be set during the execution of prepare hooks" +msgstr "" + +#, c-format +msgid "internal error, please report: running %q failed: %v\n" +msgstr "" + +msgid "internal error: cannot find attrs task" +msgstr "" + +msgid "internal error: cannot find plug or slot data in the appropriate task" +msgstr "" + +#, c-format +msgid "internal error: cannot get %s from appropriate task" +msgstr "" + +msgid "" +"invalid argument for flag ‘-n’: expected a non-negative integer argument, or " +"“all”." +msgstr "" + +#, c-format +msgid "invalid attribute: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid configuration: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid header filter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid parameter: %q (want key=value)" +msgstr "" + +#, c-format +msgid "invalid value: %q (want snap:name or snap)" +msgstr "" + +#, c-format +msgid "" +"key name %q is not valid; only ASCII letters, digits, and hyphens are allowed" +msgstr "" + +msgid "missing snap-confine: try updating your snapd package" +msgstr "" + +msgid "need the application to run as argument" +msgstr "" + +msgid "no changes found" +msgstr "" + +#, c-format +msgid "no changes of type %q found" +msgstr "" + +msgid "no interfaces currently connected" +msgstr "" + +msgid "no interfaces found" +msgstr "" + +msgid "no matching snaps installed" +msgstr "" + +msgid "no such interface" +msgstr "" + +msgid "no valid snaps given" +msgstr "" + +msgid "not a valid snap" +msgstr "" + +msgid "please provide change ID or type with --last=" +msgstr "" + +#. TRANSLATORS: if possible, a single short word +msgid "private" +msgstr "" + +msgid "" +"reboot scheduled to update the system - temporarily cancel with 'sudo " +"shutdown -c'" +msgstr "" + +#, c-format +msgid "set failed: %v" +msgstr "" + +msgid "set which option?" +msgstr "" + +msgid "show detailed information about a snap" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --revision=foo +#, c-format +msgid "snap %%q not found (at least at revision %q)" +msgstr "" + +#. TRANSLATORS: %%q will become a %q for the snap name; %q is whatever foo the user used for --channel=foo +#, c-format +msgid "snap %%q not found (at least in channel %q)" +msgstr "" + +#, c-format +msgid "snap %q has no updates available" +msgstr "" + +#, c-format +msgid "snap %q is already installed, see \"snap refresh --help\"" +msgstr "" + +#, c-format +msgid "snap %q is local" +msgstr "" + +#, c-format +msgid "snap %q not found" +msgstr "" + +#. TRANSLATORS: free as in gratis +msgid "snap is free" +msgstr "" + +msgid "too many arguments for command" +msgstr "" + +#. TRANSLATORS: %q is the hook name; %s a space-separated list of extra arguments +#, c-format +msgid "too many arguments for hook %q: %s" +msgstr "" + +#. TRANSLATORS: the %s is the list of extra arguments +#, c-format +msgid "too many arguments: %s" +msgstr "" + +msgid "unavailable" +msgstr "" + +#, c-format +msgid "unknown attribute %q" +msgstr "" + +#, c-format +msgid "unknown command %q, see \"snap --help\"" +msgstr "" + +#, c-format +msgid "unknown plug or slot %q" +msgstr "" + +#, c-format +msgid "unsupported shell %v" +msgstr "" diff -Nru snapd-2.28.5/progress/ansimeter.go snapd-2.29.3/progress/ansimeter.go --- snapd-2.28.5/progress/ansimeter.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/progress/ansimeter.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,207 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package progress + +import ( + "fmt" + "io" + "os" + "time" + "unicode" + + "golang.org/x/crypto/ssh/terminal" +) + +var stdout io.Writer = os.Stdout + +// ANSIMeter is a progress.Meter that uses ANSI escape codes to make +// better use of the available horizontal space. +type ANSIMeter struct { + label []rune + total float64 + written float64 + spin int + t0 time.Time +} + +// these are the bits of the ANSI escapes (beyond \r) that we use +// (names of the terminfo capabilities, see terminfo(5)) +var ( + // clear to end of line + clrEOL = "\033[K" + // make cursor invisible + cursorInvisible = "\033[?25l" + // make cursor visible + cursorVisible = "\033[?12;25h" + // turn on reverse video + enterReverseMode = "\033[7m" + // go back to normal video + exitAttributeMode = "\033[0m" +) + +var termWidth = func() int { + col, _, _ := terminal.GetSize(0) + if col <= 0 { + // give up + col = 80 + } + return col +} + +func (p *ANSIMeter) Start(label string, total float64) { + p.label = []rune(label) + p.total = total + p.t0 = time.Now().UTC() + fmt.Fprint(stdout, cursorInvisible) +} + +func norm(col int, msg []rune) []rune { + if col <= 0 { + return []rune{} + } + out := make([]rune, col) + copy(out, msg) + d := col - len(msg) + if d < 0 { + out[col-1] = '…' + } else { + for i := len(msg); i < col; i++ { + out[i] = ' ' + } + } + return out +} + +func (p *ANSIMeter) SetTotal(total float64) { + p.total = total +} + +func (p *ANSIMeter) percent() string { + if p.total == 0. { + return "---%" + } + q := p.written * 100 / p.total + if q > 999.4 || q < 0. { + return "???%" + } + return fmt.Sprintf("%3.0f%%", q) +} + +func (p *ANSIMeter) Set(current float64) { + if current < 0 { + current = 0 + } + if current > p.total { + current = p.total + } + + p.written = current + col := termWidth() + // time left: 5 + // gutter: 1 + // speed: 8 + // gutter: 1 + // percent: 4 + // gutter: 1 + // ===== + // 20 + // and we want to leave at least 10 for the label, so: + // * if width <= 15, don't show any of this (progress bar is good enough) + // * if 15 < width <= 20, only show time left (time left + gutter = 6) + // * if 20 < width <= 29, also show percentage (percent + gutter = 5 + // * if 29 < width , also show speed (speed+gutter = 9) + var percent, speed, timeleft string + if col > 15 { + since := time.Now().UTC().Sub(p.t0).Seconds() + per := since / p.written + left := (p.total - p.written) * per + timeleft = " " + formatDuration(left) + if col > 20 { + percent = " " + p.percent() + if col > 29 { + speed = " " + formatBPS(p.written, since, -1) + } + } + } + + msg := make([]rune, 0, col) + msg = append(msg, norm(col-len(percent)-len(speed)-len(timeleft), p.label)...) + msg = append(msg, []rune(percent)...) + msg = append(msg, []rune(speed)...) + msg = append(msg, []rune(timeleft)...) + i := int(current * float64(col) / p.total) + fmt.Fprint(stdout, "\r", enterReverseMode, string(msg[:i]), exitAttributeMode, string(msg[i:])) +} + +func (p *ANSIMeter) Spin(msgstr string) { + // spin moves a block a third of the screen's width right and + // left across the screen (each call to Spin bummps it left + // or right by 1%) + col := termWidth() + msg := norm(col, []rune(msgstr)) + p.spin++ + if p.spin > 66 { + p.spin = -p.spin + 1 + + } + spin := p.spin + if spin < 0 { + spin = -spin + } + i := spin * col / 100 + j := 1 + (spin+33)*col/100 + fmt.Fprint(stdout, "\r", string(msg[:i]), enterReverseMode, string(msg[i:j]), exitAttributeMode, string(msg[j:])) +} + +func (*ANSIMeter) Finished() { + fmt.Fprint(stdout, "\r", exitAttributeMode, cursorVisible, clrEOL) +} + +func (*ANSIMeter) Notify(msgstr string) { + col := termWidth() + fmt.Fprint(stdout, "\r", exitAttributeMode, clrEOL) + + msg := []rune(msgstr) + var i int + for len(msg) > col { + for i = col; i >= 0; i-- { + if unicode.IsSpace(msg[i]) { + break + } + } + if i < 1 { + // didn't find anything; print the whole thing and try again + fmt.Fprintln(stdout, string(msg[:col])) + msg = msg[col:] + } else { + // found a space; print up to but not including it, and skip it + fmt.Fprintln(stdout, string(msg[:i])) + msg = msg[i+1:] + } + } + fmt.Fprintln(stdout, string(msg)) +} + +func (p *ANSIMeter) Write(bs []byte) (n int, err error) { + n = len(bs) + p.Set(p.written + float64(n)) + + return +} diff -Nru snapd-2.28.5/progress/ansimeter_test.go snapd-2.29.3/progress/ansimeter_test.go --- snapd-2.28.5/progress/ansimeter_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/progress/ansimeter_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,263 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package progress_test + +import ( + "bytes" + "fmt" + "os" + "strings" + "time" + + "gopkg.in/check.v1" + + "github.com/snapcore/snapd/progress" +) + +type ansiSuite struct { + stdout *os.File +} + +var _ = check.Suite(ansiSuite{}) + +func (ansiSuite) TestNorm(c *check.C) { + msg := []rune(strings.Repeat("0123456789", 100)) + high := []rune("🤗🤗🤗🤗🤗") + c.Assert(msg, check.HasLen, 1000) + for i := 1; i < 1000; i += 1 { + long := progress.Norm(i, msg) + short := progress.Norm(i, nil) + // a long message is truncated to fit + c.Check(long, check.HasLen, i) + c.Check(long[len(long)-1], check.Equals, rune('…')) + // a short message is padded to width + c.Check(short, check.HasLen, i) + c.Check(string(short), check.Equals, strings.Repeat(" ", i)) + // high unicode? no problem + c.Check(progress.Norm(i, high), check.HasLen, i) + } + // check it doesn't panic for negative nor zero widths + c.Check(progress.Norm(0, []rune("hello")), check.HasLen, 0) + c.Check(progress.Norm(-10, []rune("hello")), check.HasLen, 0) +} + +func (ansiSuite) TestPercent(c *check.C) { + p := &progress.ANSIMeter{} + for i := -1000.; i < 1000.; i += 5 { + p.SetTotal(i) + for j := -1000.; j < 1000.; j += 3 { + p.SetWritten(j) + percent := p.Percent() + c.Check(percent, check.HasLen, 4) + c.Check(percent[len(percent)-1:], check.Equals, "%") + } + } +} + +func (ansiSuite) TestStart(c *check.C) { + var buf bytes.Buffer + defer progress.MockStdout(&buf)() + defer progress.MockTermWidth(func() int { return 80 })() + + p := &progress.ANSIMeter{} + p.Start("0123456789", 100) + c.Check(p.GetTotal(), check.Equals, 100.) + c.Check(p.GetWritten(), check.Equals, 0.) + c.Check(buf.String(), check.Equals, progress.CursorInvisible) +} + +func (ansiSuite) TestFinish(c *check.C) { + var buf bytes.Buffer + defer progress.MockStdout(&buf)() + defer progress.MockTermWidth(func() int { return 80 })() + p := &progress.ANSIMeter{} + p.Finished() + c.Check(buf.String(), check.Equals, fmt.Sprint( + "\r", // move cursor to start of line + progress.ExitAttributeMode, // turn off color, reverse, bold, anything + progress.CursorVisible, // turn the cursor back on + progress.ClrEOL, // and clear the rest of the line + )) +} + +func (ansiSuite) TestSetLayout(c *check.C) { + var buf bytes.Buffer + var width int + defer progress.MockStdout(&buf)() + defer progress.MockEmptyEscapes()() + defer progress.MockTermWidth(func() int { return width })() + + p := &progress.ANSIMeter{} + msg := "0123456789" + ticker := time.NewTicker(time.Millisecond) + defer ticker.Stop() + p.Start(msg, 1E300) + for i := 1; i <= 80; i++ { + desc := check.Commentf("width %d", i) + width = i + buf.Reset() + <-ticker.C + p.Set(float64(i)) + out := buf.String() + c.Check([]rune(out), check.HasLen, i+1, desc) + switch { + case i < len(msg): + c.Check(out, check.Equals, "\r"+msg[:i-1]+"…", desc) + case i <= 15: + c.Check(out, check.Equals, fmt.Sprintf("\r%*s", -i, msg), desc) + case i <= 20: + c.Check(out, check.Equals, fmt.Sprintf("\r%*s ages!", -(i-6), msg), desc) + case i <= 29: + c.Check(out, check.Equals, fmt.Sprintf("\r%*s 0%% ages!", -(i-11), msg), desc) + default: + c.Check(out, check.Matches, fmt.Sprintf("\r%*s 0%% [ 0-9]{4}B/s ages!", -(i-20), msg), desc) + } + } +} + +func (ansiSuite) TestSetEscapes(c *check.C) { + var buf bytes.Buffer + defer progress.MockStdout(&buf)() + defer progress.MockSimpleEscapes()() + defer progress.MockTermWidth(func() int { return 10 })() + + p := &progress.ANSIMeter{} + msg := "0123456789" + p.Start(msg, 10) + for i := 0.; i <= 10; i++ { + buf.Reset() + p.Set(i) + // here we're using the fact that the message has the same + // length as p's total to make the test simpler :-) + expected := "\r" + msg[:int(i)] + "" + msg[int(i):] + c.Check(buf.String(), check.Equals, expected, check.Commentf("%g", i)) + } +} + +func (ansiSuite) TestSpinEscapes(c *check.C) { + var buf bytes.Buffer + defer progress.MockStdout(&buf)() + defer progress.MockSimpleEscapes()() + defer progress.MockTermWidth(func() int { return 100 })() + + p := &progress.ANSIMeter{} + msg := strings.Repeat("0123456789", 10) + c.Assert(len(msg), check.Equals, 100) + p.Start(msg, 10) + for i := 1; i <= 66; i++ { + buf.Reset() + p.Spin(msg) + expected := "\r" + msg[:i] + "" + msg[i:i+34] + "" + msg[i+34:] + c.Check(buf.String(), check.Equals, expected, check.Commentf("%d", i)) + } + for i := 0; i <= 66; i++ { + buf.Reset() + p.Spin(msg) + expected := "\r" + msg[:66-i] + "" + msg[66-i:100-i] + "" + msg[100-i:] + c.Check(buf.String(), check.Equals, expected, check.Commentf("%d", -i)) + } +} + +func (ansiSuite) TestNotify(c *check.C) { + var buf bytes.Buffer + var width int + defer progress.MockStdout(&buf)() + defer progress.MockSimpleEscapes()() + defer progress.MockTermWidth(func() int { return width })() + + p := &progress.ANSIMeter{} + p.Start("working", 1E300) + + width = 10 + p.Set(0) + p.Notify("hello there") + p.Set(1) + c.Check(buf.String(), check.Equals, ""+ // the VI from Start() + "\rworking "+ // the Set(0) + "\rhello\n"+ // first line of the Notify (note it wrapped at word end) + "there\n"+ + "\rworking ") // the Set(1) + + buf.Reset() + p.Set(0) + p.Notify("supercalifragilisticexpialidocious") + p.Set(1) + c.Check(buf.String(), check.Equals, ""+ // no Start() this time + "\rworking "+ // the Set(0) + "\rsupercalif\n"+ // the Notify, word is too long so it's just split + "ragilistic\n"+ + "expialidoc\n"+ + "ious\n"+ + "\rworking ") // the Set(1) + + buf.Reset() + width = 16 + p.Set(0) + p.Notify("hello there") + p.Set(1) + c.Check(buf.String(), check.Equals, ""+ // no Start() + "\rworking ages!"+ // the Set(0) + "\rhello there\n"+ // first line of the Notify (no wrap!) + "\rworking ages!") // the Set(1) + +} + +func (ansiSuite) TestSpin(c *check.C) { + var buf bytes.Buffer + defer progress.MockStdout(&buf)() + defer progress.MockEmptyEscapes()() + defer progress.MockTermWidth(func() int { return 10 })() + + t := &progress.ANSIMeter{} + for i := 0; i < 6; i++ { + t.Spin("msg") + } + + c.Assert(buf.String(), check.Equals, strings.Repeat(fmt.Sprintf("\r%-10s", "msg"), 6)) +} + +func (ansiSuite) TestWrite(c *check.C) { + var buf bytes.Buffer + defer progress.MockStdout(&buf)() + defer progress.MockSimpleEscapes()() + defer progress.MockTermWidth(func() int { return 10 })() + + p := &progress.ANSIMeter{} + p.Start("123456789x", 10) + for i := 0; i < 10; i++ { + n, err := fmt.Fprintf(p, "%d", i) + c.Assert(err, check.IsNil) + c.Check(n, check.Equals, 1) + } + + c.Check(buf.String(), check.Equals, strings.Join([]string{ + "", // Start() + "\r123456789x", + "\r123456789x", + "\r123456789x", + "\r123456789x", + "\r123456789x", + "\r123456789x", + "\r123456789x", + "\r123456789x", + "\r123456789x", + "\r123456789x", + }, "")) +} diff -Nru snapd-2.28.5/progress/example_test.go snapd-2.29.3/progress/example_test.go --- snapd-2.28.5/progress/example_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/progress/example_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,112 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package progress_test + +import ( + "fmt" + "math" + "time" + + "github.com/snapcore/snapd/progress" +) + +func ExampleFormatAmount_short() { + fmt.Printf("%q\n", progress.FormatAmount(12345, -1)) + // Output: "12.3k" +} + +func ExampleFormatAmount_long() { + for _, amount := range []uint64{ + 3, + 13, 95, + 103, 995, + 1013, 9995, + 10009, 99995, + } { + fmt.Printf("- %5d: 3: %q 5: %q 7: %q\n", + amount, + progress.FormatAmount(amount, 3), + progress.FormatAmount(amount, -1), + progress.FormatAmount(amount, 7), + ) + } + // Output: + // - 3: 3: " 3" 5: " 3" 7: " 3 " + // - 13: 3: " 13" 5: " 13" 7: " 13 " + // - 95: 3: " 95" 5: " 95" 7: " 95 " + // - 103: 3: "103" 5: " 103" 7: " 103 " + // - 995: 3: "995" 5: " 995" 7: " 995 " + // - 1013: 3: " 1k" 5: " 1013" 7: " 1013 " + // - 9995: 3: "10k" 5: "10.0k" 7: " 9.995k" + // - 10009: 3: "10k" 5: "10.0k" 7: "10.009k" + // - 99995: 3: ".1M" 5: " 100k" 7: "100.00k" +} + +func ExampleFormatBPS() { + fmt.Printf("%q\n", progress.FormatBPS(12345, (10*time.Millisecond).Seconds(), -1)) + // Output: "1.23MB/s" +} + +func ExampleFormatDuration() { + for _, dt := range []time.Duration{ + 3 * time.Nanosecond, + 36 * time.Microsecond, + 430 * time.Millisecond, + 5155 * time.Millisecond, + time.Minute + 2*time.Second, + 124 * time.Minute / 10, + 2*time.Hour + 29*time.Minute, + 10*time.Hour + 9*time.Minute, + 10*time.Hour + 30*time.Minute, + 11*time.Hour + 2*time.Minute, + 30 * time.Hour, + 345 * time.Hour, + 357 * time.Hour, + 4272 * time.Hour, + 51368 * time.Hour, + math.MaxInt64 / 10, + math.MaxInt64, + } { + fmt.Printf("%q\n", progress.FormatDuration(dt.Seconds())) + } + fmt.Printf("%q\n", progress.FormatDuration(float64(math.MaxUint64)*365*24*60*60)) + fmt.Printf("%q\n", progress.FormatDuration(math.MaxFloat64)) + + // Output: + // "3.0ns" + // " 36µs" + // "430ms" + // "5.16s" + // "1m02s" + // "12.4m" + // "2h29m" + // "10h9m" + // "10.5h" + // "11h2m" + // "1d06h" + // "14d9h" + // "14.9d" + // " 178d" + // "5.86y" + // "29.2y" + // " 292y" + // " 18Ey" + // "ages!" +} diff -Nru snapd-2.28.5/progress/export_test.go snapd-2.29.3/progress/export_test.go --- snapd-2.28.5/progress/export_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/progress/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,103 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package progress + +import ( + "io" +) + +var FormatAmount = formatAmount +var FormatBPS = formatBPS +var FormatDuration = formatDuration + +var ( + ClrEOL = clrEOL + CursorInvisible = cursorInvisible + CursorVisible = cursorVisible + EnterReverseMode = enterReverseMode + ExitAttributeMode = exitAttributeMode +) + +func MockEmptyEscapes() func() { + clrEOL = "" + cursorInvisible = "" + cursorVisible = "" + enterReverseMode = "" + exitAttributeMode = "" + + return func() { + clrEOL = ClrEOL + cursorInvisible = CursorInvisible + cursorVisible = CursorVisible + enterReverseMode = EnterReverseMode + exitAttributeMode = ExitAttributeMode + } +} + +func MockSimpleEscapes() func() { + // set them to the tcap name (in all caps) + clrEOL = "" + cursorInvisible = "" + cursorVisible = "" + enterReverseMode = "" + exitAttributeMode = "" + + return func() { + clrEOL = ClrEOL + cursorInvisible = CursorInvisible + cursorVisible = CursorVisible + enterReverseMode = EnterReverseMode + exitAttributeMode = ExitAttributeMode + } +} + +func (p *ANSIMeter) Percent() string { + return p.percent() +} + +func (p *ANSIMeter) SetWritten(written float64) { + p.written = written +} + +func (p *ANSIMeter) GetWritten() float64 { + return p.written +} + +func (p *ANSIMeter) GetTotal() float64 { + return p.total +} + +func MockTermWidth(f func() int) func() { + origTermWidth := termWidth + termWidth = f + return func() { + termWidth = origTermWidth + } +} + +func MockStdout(w io.Writer) func() { + origStdout := stdout + stdout = w + return func() { + stdout = origStdout + } +} + +var Norm = norm diff -Nru snapd-2.28.5/progress/progress.go snapd-2.29.3/progress/progress.go --- snapd-2.28.5/progress/progress.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/progress/progress.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,13 +20,11 @@ package progress import ( - "bufio" "fmt" "os" - "unicode" + "strings" "golang.org/x/crypto/ssh/terminal" - "gopkg.in/cheggaaa/pb.v1" ) // Meter is an interface to show progress to the user @@ -53,147 +51,55 @@ Notify(string) } -// NullProgress is a Meter that does nothing -type NullProgress struct { -} - -// Start does nothing -func (t *NullProgress) Start(label string, total float64) { -} - -// Set does nothing -func (t *NullProgress) Set(current float64) { -} - -// SetTotal does nothing -func (t *NullProgress) SetTotal(total float64) { -} - -// Finished does nothing -func (t *NullProgress) Finished() { -} - -// Write does nothing -func (t *NullProgress) Write(p []byte) (n int, err error) { - return len(p), nil -} - -// Notify does nothing -func (t *NullProgress) Notify(string) {} - -// Spin does nothing -func (t *NullProgress) Spin(msg string) { -} - -const clearUntilEOL = "\033[K" - -// TextProgress show progress on the terminal -type TextProgress struct { - Meter - pbar *pb.ProgressBar - spinStep int -} - -// NewTextProgress returns a new TextProgress type -func NewTextProgress() *TextProgress { - return &TextProgress{} -} +// NullMeter is a Meter that does nothing +type NullMeter struct{} -// Start starts showing progress -func (t *TextProgress) Start(label string, total float64) { - t.pbar = pb.New64(int64(total)) - t.pbar.ShowSpeed = true - t.pbar.Units = pb.U_BYTES - t.pbar.Prefix(label) - t.pbar.Start() -} +// Null is a default NullMeter instance +var Null = NullMeter{} -// Set sets the progress to the current value -func (t *TextProgress) Set(current float64) { - t.pbar.Set(int(current)) -} +func (NullMeter) Start(string, float64) {} +func (NullMeter) Set(float64) {} +func (NullMeter) SetTotal(float64) {} +func (NullMeter) Finished() {} +func (NullMeter) Write(p []byte) (int, error) { return len(p), nil } +func (NullMeter) Notify(string) {} +func (NullMeter) Spin(msg string) {} -// SetTotal set the total steps needed -func (t *TextProgress) SetTotal(total float64) { - t.pbar.Total = int64(total) -} +// QuietMeter is a Meter that _just_ shows Notify()s. +type QuietMeter struct{ NullMeter } -// Finished stops displaying the progress -func (t *TextProgress) Finished() { - if t.pbar != nil { - // workaround silly pb that always does a fmt.Println() on - // finish (unless NotPrint is set) - t.pbar.NotPrint = true - t.pbar.Finish() - t.pbar.NotPrint = false - } - fmt.Printf("\r\033[K") +func (QuietMeter) Notify(msg string) { + fmt.Fprintln(stdout, msg) } -// Write is there so that progress can implment a Writer and can be -// used to display progress of io operations -func (t *TextProgress) Write(p []byte) (n int, err error) { - return t.pbar.Write(p) -} +// testMeter, if set, is returned by MakeProgressBar; set it from tests. +var testMeter Meter -// Spin advances a spinner, i.e. can be used to show progress for operations -// that have a unknown duration -func (t *TextProgress) Spin(msg string) { - states := `|/-\` - - // clear until end of line - fmt.Printf("\r[%c] %s%s", states[t.spinStep], msg, clearUntilEOL) - t.spinStep++ - if t.spinStep >= len(states) { - t.spinStep = 0 +func MockMeter(meter Meter) func() { + testMeter = meter + return func() { + testMeter = nil } } -// Agreed asks the user whether they agree to the given license text -func (t *TextProgress) Agreed(intro, license string) bool { - if _, err := fmt.Println(intro); err != nil { - return false - } - - // XXX: send it through a pager instead of this ugly thing - if _, err := fmt.Println(license); err != nil { - return false - } +var inTesting bool = len(os.Args) > 0 && strings.HasSuffix(os.Args[0], ".test") || os.Getenv("SPREAD_SYSTEM") != "" - reader := bufio.NewReader(os.Stdin) - if _, err := fmt.Print("Do you agree? [y/n] "); err != nil { - return false - } - r, _, err := reader.ReadRune() - if err != nil { - return false - } - - return unicode.ToLower(r) == 'y' -} - -// Notify the user of miscellaneous events -func (*TextProgress) Notify(msg string) { - fmt.Printf("\r%s%s\n", msg, clearUntilEOL) -} - -// MakeProgressBar creates an appropriate progress (which may be a -// NullProgress bar if there is no associated terminal). +// MakeProgressBar creates an appropriate progress.Meter for the environ in +// which it is called: +// +// * if MockMeter has been called, return that. +// * if no terminal is attached, or we think we're running a test, a +// minimalistic QuietMeter is returned. +// * otherwise, an ANSIMeter is returned. +// +// TODO: instead of making the pivot at creation time, do it at every call. func MakeProgressBar() Meter { - var pbar Meter - if attachedToTerminal() { - pbar = NewTextProgress() - } else { - pbar = &NullProgress{} + if testMeter != nil { + return testMeter + } + if !inTesting && terminal.IsTerminal(int(os.Stdin.Fd())) { + return &ANSIMeter{} } - return pbar -} - -// attachedToTerminal returns true if the calling process is attached to -// a terminal device. -var attachedToTerminal = func() bool { - fd := int(os.Stdin.Fd()) - - return terminal.IsTerminal(fd) + return QuietMeter{} } diff -Nru snapd-2.28.5/progress/progresstest/progresstest.go snapd-2.29.3/progress/progresstest/progresstest.go --- snapd-2.28.5/progress/progresstest/progresstest.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/progress/progresstest/progresstest.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,68 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package progresstest + +import ( + "github.com/snapcore/snapd/progress" +) + +type Meter struct { + Labels []string + Totals []float64 + Values []float64 + Written [][]byte + Notices []string + Finishes int +} + +// interface check +var _ progress.Meter = (*Meter)(nil) + +func (p *Meter) Start(label string, total float64) { + p.Spin(label) + p.SetTotal(total) +} + +func (p *Meter) Set(value float64) { + p.Values = append(p.Values, value) +} + +func (p *Meter) SetTotal(total float64) { + p.Totals = append(p.Totals, total) +} + +func (p *Meter) Finished() { + p.Finishes++ +} + +func (p *Meter) Spin(label string) { + p.Labels = append(p.Labels, label) +} + +func (p *Meter) Write(bs []byte) (n int, err error) { + p.Written = append(p.Written, bs) + n = len(bs) + + return +} + +func (p *Meter) Notify(msg string) { + p.Notices = append(p.Notices, msg) +} diff -Nru snapd-2.28.5/progress/progress_test.go snapd-2.29.3/progress/progress_test.go --- snapd-2.28.5/progress/progress_test.go 2016-05-03 05:50:48.000000000 +0000 +++ snapd-2.29.3/progress/progress_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -17,134 +17,49 @@ * */ -package progress +package progress_test import ( + "bytes" "fmt" - "io/ioutil" - "os" "testing" . "gopkg.in/check.v1" + + "github.com/snapcore/snapd/progress" + "github.com/snapcore/snapd/progress/progresstest" ) // Hook up check.v1 into the "go test" runner func Test(t *testing.T) { TestingT(t) } type ProgressTestSuite struct { - attachedToTerminalReturn bool - - originalAttachedToTerminal func() bool } var _ = Suite(&ProgressTestSuite{}) -func (ts *ProgressTestSuite) MockAttachedToTerminal() bool { - return ts.attachedToTerminalReturn -} - -func (ts *ProgressTestSuite) TestSpin(c *C) { - f, err := ioutil.TempFile("", "progress-") - c.Assert(err, IsNil) - defer os.Remove(f.Name()) - oldStdout := os.Stdout - os.Stdout = f - - t := NewTextProgress() - for i := 0; i < 6; i++ { - t.Spin("msg") - } - - os.Stdout = oldStdout - f.Sync() - f.Seek(0, 0) - progress, err := ioutil.ReadAll(f) - c.Assert(err, IsNil) - c.Assert(string(progress), Equals, "\r[|] msg\x1b[K\r[/] msg\x1b[K\r[-] msg\x1b[K\r[\\] msg\x1b[K\r[|] msg\x1b[K\r[/] msg\x1b[K") -} - -func (ts *ProgressTestSuite) testAgreed(answer string, value bool, c *C) { - fout, err := ioutil.TempFile("", "progress-out-") - c.Assert(err, IsNil) - oldStdout := os.Stdout - os.Stdout = fout - defer func() { - os.Stdout = oldStdout - os.Remove(fout.Name()) - fout.Close() - }() - - fin, err := ioutil.TempFile("", "progress-in-") - c.Assert(err, IsNil) - oldStdin := os.Stdin - os.Stdin = fin - defer func() { - os.Stdin = oldStdin - os.Remove(fin.Name()) - fin.Close() - }() - - _, err = fmt.Fprintln(fin, answer) - c.Assert(err, IsNil) - _, err = fin.Seek(0, 0) - c.Assert(err, IsNil) - - license := "Void where empty." - - t := NewTextProgress() - c.Check(t.Agreed("blah blah", license), Equals, value) - - _, err = fout.Seek(0, 0) - c.Assert(err, IsNil) - out, err := ioutil.ReadAll(fout) - c.Assert(err, IsNil) - c.Check(string(out), Equals, "blah blah\n"+license+"\nDo you agree? [y/n] ") -} +func (ts *ProgressTestSuite) testNotify(c *C, t progress.Meter, desc, expected string) { + var buf bytes.Buffer + defer progress.MockStdout(&buf)() -func (ts *ProgressTestSuite) TestAgreed(c *C) { - ts.testAgreed("Y", true, c) - ts.testAgreed("N", false, c) -} + comment := Commentf(desc) -func (ts *ProgressTestSuite) TestNotify(c *C) { - fout, err := ioutil.TempFile("", "notify-out-") - c.Assert(err, IsNil) - oldStdout := os.Stdout - os.Stdout = fout - defer func() { - os.Stdout = oldStdout - os.Remove(fout.Name()) - fout.Close() - }() - - t := NewTextProgress() t.Notify("blah blah") - _, err = fout.Seek(0, 0) - c.Assert(err, IsNil) - out, err := ioutil.ReadAll(fout) - c.Assert(err, IsNil) - c.Check(string(out), Equals, fmt.Sprintf("\rblah blah%s\n", clearUntilEOL)) + c.Check(buf.String(), Equals, expected, comment) } -func (ts *ProgressTestSuite) TestMakeProgressBar(c *C) { - var pbar Meter - - ts.originalAttachedToTerminal = attachedToTerminal - attachedToTerminal = ts.MockAttachedToTerminal - defer func() { - // reset - attachedToTerminal = ts.originalAttachedToTerminal - }() - - ts.attachedToTerminalReturn = true - - pbar = MakeProgressBar() - c.Assert(pbar, FitsTypeOf, NewTextProgress()) - - ts.attachedToTerminalReturn = false +func (ts *ProgressTestSuite) TestQuietNotify(c *C) { + ts.testNotify(c, &progress.QuietMeter{}, "quiet", "blah blah\n") +} - pbar = MakeProgressBar() - c.Assert(pbar, FitsTypeOf, &NullProgress{}) +func (ts *ProgressTestSuite) TestANSINotify(c *C) { + expected := fmt.Sprint("\r", progress.ExitAttributeMode, progress.ClrEOL, "blah blah\n") + ts.testNotify(c, &progress.ANSIMeter{}, "ansi", expected) +} +func (ts *ProgressTestSuite) TestTestingNotify(c *C) { + p := &progresstest.Meter{} + ts.testNotify(c, p, "test", "") + c.Check(p.Notices, DeepEquals, []string{"blah blah"}) } diff -Nru snapd-2.28.5/progress/quantity.go snapd-2.29.3/progress/quantity.go --- snapd-2.28.5/progress/quantity.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/progress/quantity.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,202 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package progress + +import ( + "fmt" + "math" + + "github.com/snapcore/snapd/i18n" +) + +// these are taken from github.com/chipaca/quantity with permission :-) + +func formatAmount(amount uint64, width int) string { + if width < 0 { + width = 5 + } + max := uint64(5000) + maxFloat := 999.5 + + if width < 4 { + width = 3 + max = 999 + maxFloat = 99.5 + } + + if amount <= max { + pad := "" + if width > 5 { + pad = " " + } + return fmt.Sprintf("%*d%s", width-len(pad), amount, pad) + } + var prefix rune + r := float64(amount) + // zetta and yotta are me being pedantic: maxuint64 is ~18EB + for _, prefix = range "kMGTPEZY" { + r /= 1000 + if r < maxFloat { + break + } + } + + width-- + digits := 3 + if r < 99.5 { + digits-- + if r < 9.5 { + digits-- + if r < .95 { + digits-- + } + } + } + precision := 0 + if (width - digits) > 1 { + precision = width - digits - 1 + } + + s := fmt.Sprintf("%*.*f%c", width, precision, r, prefix) + if r < .95 { + return s[1:] + } + return s +} + +func formatBPS(n, sec float64, width int) string { + if sec < 0 { + sec = -sec + } + return formatAmount(uint64(n/sec), width-2) + "B/s" +} + +const ( + period = 365.25 // julian years (c.f. the actual orbital period, 365.256363004d) +) + +func divmod(a, b float64) (q, r float64) { + q = math.Floor(a / b) + return q, a - q*b +} + +var ( + // TRANSLATORS: this needs to be a single rune that is understood to mean "seconds" in e.g. 1m30s + // (I fully expect this to always be "s", given it's a SI unit) + secs = i18n.G("s") + // TRANSLATORS: this needs to be a single rune that is understood to mean "minutes" in e.g. 1m30s + mins = i18n.G("m") + // TRANSLATORS: this needs to be a single rune that is understood to mean "hours" in e.g. 1h30m + hours = i18n.G("h") + // TRANSLATORS: this needs to be a single rune that is understood to mean "days" in e.g. 1d20h + days = i18n.G("d") + // TRANSLATORS: this needs to be a single rune that is understood to mean "years" in e.g. 1y45d + years = i18n.G("y") +) + +// dt is seconds (as in the output of time.Now().Seconds()) +func formatDuration(dt float64) string { + if dt < 60 { + if dt >= 9.995 { + return fmt.Sprintf("%.1f%s", dt, secs) + } else if dt >= .9995 { + return fmt.Sprintf("%.2f%s", dt, secs) + } + + var prefix rune + for _, prefix = range "mµn" { + dt *= 1000 + if dt >= .9995 { + break + } + } + + if dt > 9.5 { + return fmt.Sprintf("%3.f%c%s", dt, prefix, secs) + } + + return fmt.Sprintf("%.1f%c%s", dt, prefix, secs) + } + + if dt < 600 { + m, s := divmod(dt, 60) + return fmt.Sprintf("%.f%s%02.f%s", m, mins, s, secs) + } + + dt /= 60 // dt now minutes + + if dt < 99.95 { + return fmt.Sprintf("%3.1f%s", dt, mins) + } + + if dt < 10*60 { + h, m := divmod(dt, 60) + return fmt.Sprintf("%.f%s%02.f%s", h, hours, m, mins) + } + + if dt < 24*60 { + if h, m := divmod(dt, 60); m < 10 { + return fmt.Sprintf("%.f%s%1.f%s", h, hours, m, mins) + } + + return fmt.Sprintf("%3.1f%s", dt/60, hours) + } + + dt /= 60 // dt now hours + + if dt < 10*24 { + d, h := divmod(dt, 24) + return fmt.Sprintf("%.f%s%02.f%s", d, days, h, hours) + } + + if dt < 99.95*24 { + if d, h := divmod(dt, 24); h < 10 { + return fmt.Sprintf("%.f%s%.f%s", d, days, h, hours) + } + return fmt.Sprintf("%4.1f%s", dt/24, days) + } + + dt /= 24 // dt now days + + if dt < 2*period { + return fmt.Sprintf("%4.0f%s", dt, days) + } + + dt /= period // dt now years + + if dt < 9.995 { + return fmt.Sprintf("%4.2f%s", dt, years) + } + + if dt < 99.95 { + return fmt.Sprintf("%4.1f%s", dt, years) + } + + if dt < 999.5 { + return fmt.Sprintf("%4.f%s", dt, years) + } + + if dt > math.MaxUint64 || uint64(dt) == 0 { + // TODO: figure out exactly what overflow causes the ==0 + return "ages!" + } + + return formatAmount(uint64(dt), 4) + years +} diff -Nru snapd-2.28.5/PULL_REQUEST_TEMPLATE.md snapd-2.29.3/PULL_REQUEST_TEMPLATE.md --- snapd-2.28.5/PULL_REQUEST_TEMPLATE.md 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/PULL_REQUEST_TEMPLATE.md 2017-10-23 06:17:27.000000000 +0000 @@ -1,2 +1,2 @@ Thanks for helping us make a better snapd! -Have you signed the [license agreement](https://www.ubuntu.com/legal/contributors) and read the [contribution guide](CONTRIBUTING.md)? +Have you signed the [license agreement](https://www.ubuntu.com/legal/contributors) and read the [contribution guide](https://github.com/snapcore/snapd/blob/master/CONTRIBUTING.md)? diff -Nru snapd-2.28.5/release/apparmor.go snapd-2.29.3/release/apparmor.go --- snapd-2.28.5/release/apparmor.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/release/apparmor.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,117 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2014-2015 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 . + * + */ + +package release + +import ( + "fmt" + "os" + "path/filepath" + "strings" +) + +// ApparmorLevelType encodes the kind of support for apparmor +// found on this system. +type AppArmorLevelType int + +const ( + // NoAppArmor indicates that apparmor is not enabled. + NoAppArmor AppArmorLevelType = iota + // PartialAppArmor indicates that apparmor is enabled but some + // features are missing. + PartialAppArmor + // FullAppArmor indicates that all features are supported. + FullAppArmor +) + +var ( + appArmorLevel AppArmorLevelType + appArmorSummary string +) + +func init() { + appArmorLevel, appArmorSummary = probeAppArmor() +} + +// AppArmorLevel quantifies how well apparmor is supported on the +// current kernel. +func AppArmorLevel() AppArmorLevelType { + return appArmorLevel +} + +// AppArmorSummary describes how well apparmor is supported on the +// current kernel. +func AppArmorSummary() string { + return appArmorSummary +} + +// MockAppArmorSupportLevel makes the system believe it has certain +// level of apparmor support. +func MockAppArmorLevel(level AppArmorLevelType) (restore func()) { + oldAppArmorLevel := appArmorLevel + oldAppArmorSummary := appArmorSummary + appArmorLevel = level + appArmorSummary = fmt.Sprintf("mocked apparmor level: %v", level) + return func() { + appArmorLevel = oldAppArmorLevel + appArmorSummary = oldAppArmorSummary + } +} + +// probe related code +var ( + appArmorFeaturesSysPath = "/sys/kernel/security/apparmor/features" + requiredAppArmorFeatures = []string{ + "caps", + "dbus", + "domain", + "file", + "mount", + "namespaces", + "network", + "ptrace", + "signal", + } +) + +// isDirectoy is like osutil.IsDirectory but we cannot import this +// because of import cycles +func isDirectory(path string) bool { + stat, err := os.Stat(path) + if err != nil { + return false + } + return stat.IsDir() +} + +func probeAppArmor() (AppArmorLevelType, string) { + if !isDirectory(appArmorFeaturesSysPath) { + return NoAppArmor, "apparmor not enabled" + } + var missing []string + for _, feature := range requiredAppArmorFeatures { + if !isDirectory(filepath.Join(appArmorFeaturesSysPath, feature)) { + missing = append(missing, feature) + } + } + if len(missing) > 0 { + return PartialAppArmor, fmt.Sprintf("apparmor is enabled but some features are missing: %s", strings.Join(missing, ", ")) + } + return FullAppArmor, "apparmor is enabled and all features are available" +} diff -Nru snapd-2.28.5/release/apparmor_test.go snapd-2.29.3/release/apparmor_test.go --- snapd-2.28.5/release/apparmor_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/release/apparmor_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,75 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package release_test + +import ( + "os" + "path/filepath" + + . "gopkg.in/check.v1" + + "github.com/snapcore/snapd/release" +) + +type apparmorSuite struct{} + +var _ = Suite(&apparmorSuite{}) + +func (s *apparmorSuite) TestMockAppArmorLevel(c *C) { + for _, lvl := range []release.AppArmorLevelType{release.NoAppArmor, release.PartialAppArmor, release.FullAppArmor} { + restore := release.MockAppArmorLevel(lvl) + defer restore() + c.Check(release.AppArmorLevel(), Equals, lvl) + } +} + +func (s *apparmorSuite) TestProbeAppArmorNoAppArmor(c *C) { + restore := release.MockAppArmorFeaturesSysPath("/does/not/exists") + defer restore() + + level, summary := release.ProbeAppArmor() + c.Check(level, Equals, release.NoAppArmor) + c.Check(summary, Equals, "apparmor not enabled") +} + +func (s *apparmorSuite) TestProbeAppArmorPartialAppArmor(c *C) { + fakeSysPath := c.MkDir() + restore := release.MockAppArmorFeaturesSysPath(fakeSysPath) + defer restore() + + level, summary := release.ProbeAppArmor() + c.Check(level, Equals, release.PartialAppArmor) + c.Check(summary, Equals, "apparmor is enabled but some features are missing: caps, dbus, domain, file, mount, namespaces, network, ptrace, signal") +} + +func (s *apparmorSuite) TestProbeAppArmorFullAppArmor(c *C) { + fakeSysPath := c.MkDir() + restore := release.MockAppArmorFeaturesSysPath(fakeSysPath) + defer restore() + + for _, feature := range release.RequiredAppArmorFeatures { + err := os.Mkdir(filepath.Join(fakeSysPath, feature), 0755) + c.Assert(err, IsNil) + } + + level, summary := release.ProbeAppArmor() + c.Check(level, Equals, release.FullAppArmor) + c.Check(summary, Equals, "apparmor is enabled and all features are available") +} diff -Nru snapd-2.28.5/release/export_test.go snapd-2.29.3/release/export_test.go --- snapd-2.28.5/release/export_test.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/release/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -32,10 +32,15 @@ } } -func MockApparmorFeaturesSysPath(path string) (restorer func()) { - old := apparmorFeaturesSysPath - apparmorFeaturesSysPath = path +func MockAppArmorFeaturesSysPath(path string) (restorer func()) { + old := appArmorFeaturesSysPath + appArmorFeaturesSysPath = path return func() { - apparmorFeaturesSysPath = old + appArmorFeaturesSysPath = old } } + +var ( + ProbeAppArmor = probeAppArmor + RequiredAppArmorFeatures = requiredAppArmorFeatures +) diff -Nru snapd-2.28.5/release/release.go snapd-2.29.3/release/release.go --- snapd-2.28.5/release/release.go 2017-10-10 16:16:09.000000000 +0000 +++ snapd-2.29.3/release/release.go 2017-10-23 06:17:27.000000000 +0000 @@ -25,7 +25,6 @@ "strings" "unicode" - "github.com/snapcore/snapd/apparmor" "github.com/snapcore/snapd/strutil" ) @@ -39,26 +38,10 @@ VersionID string `json:"version-id,omitempty"` } -var ( - apparmorFeaturesSysPath = "/sys/kernel/security/apparmor/features" - requiredApparmorFeatures = []string{ - "caps", - "dbus", - "domain", - "file", - "mount", - "namespaces", - "network", - "ptrace", - "signal", - } -) - // ForceDevMode returns true if the distribution doesn't implement required // security features for confinement and devmode is forced. func (o *OS) ForceDevMode() bool { - level, _ := apparmor.ProbeKernel().Evaluate() - return level != apparmor.Full + return AppArmorLevel() != FullAppArmor } func DistroLike(distros ...string) bool { @@ -157,9 +140,9 @@ // MockForcedDevmode fake the system to believe its in a distro // that is in ForcedDevmode func MockForcedDevmode(isDevmode bool) (restore func()) { - level := apparmor.Full + level := FullAppArmor if isDevmode { - level = apparmor.None + level = NoAppArmor } - return apparmor.MockFeatureLevel(level) + return MockAppArmorLevel(level) } diff -Nru snapd-2.28.5/run-checks snapd-2.29.3/run-checks --- snapd-2.28.5/run-checks 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/run-checks 2017-10-23 06:17:27.000000000 +0000 @@ -158,6 +158,16 @@ fi # ineffassign knows about ignoring vendor/ \o/ ineffassign . + + echo Checking for naked returns + if ! which nakedret >/dev/null; then + go get -u github.com/alexkohler/nakedret + fi + got=$(nakedret ./... 2>&1) + if [ -n "$got" ]; then + echo "$got" + exit 1 + fi fi if [ "$UNIT" = 1 ]; then diff -Nru snapd-2.28.5/snap/epoch.go snapd-2.29.3/snap/epoch.go --- snapd-2.28.5/snap/epoch.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/snap/epoch.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,297 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package snap + +import ( + "encoding/json" + "fmt" + "sort" + "strconv" + + "github.com/snapcore/snapd/logger" +) + +// An Epoch represents the ability of the snap to read and write its data. Most +// developers need not worry about it, and snaps default to the 0th epoch, and +// users are only offered refreshes to epoch 0 snaps. Once an epoch bump is in +// order, there's a simplified expression they can use which should cover the +// majority of the cases: +// +// epoch: N +// +// means a snap can read/write exactly the Nth epoch's data, and +// +// epoch: N* +// +// means a snap can additionally read (N-1)th epoch's data, which means it's a +// snap that can migrate epochs (so a user on epoch 0 can get offered a refresh +// to a snap on epoch 1*). +// +// If the above is not enough, a developer can explicitly describe what epochs a +// snap can read and write: +// +// epoch: +// read: [1, 2, 3] +// write: [1, 3] +// +// the read attribute defaults to the value of the write attribute, and the +// write attribute defaults to the last item in the read attribute. If both are +// unset, it's the same as not specifying an epoch at all (i.e. epoch: 0). The +// lists must not have more than 10 elements, they must be in ascending order, +// and there must be a non-empty intersection between them. +// +// Epoch numbers must be written in base 10, with no zero padding. +type Epoch struct { + Read []uint32 `yaml:"read"` + Write []uint32 `yaml:"write"` +} + +// E returns the epoch represented by the expression s. It's meant for use in +// testing, as it panics at the first sign of trouble. +func E(s string) *Epoch { + var e Epoch + if err := e.fromString(s); err != nil { + panic(fmt.Errorf("%q: %v", s, err)) + } + return &e +} + +func (e *Epoch) fromString(s string) error { + if len(s) == 0 || s == "0" { + e.Read = []uint32{0} + e.Write = []uint32{0} + return nil + } + star := false + if s[len(s)-1] == '*' { + star = true + s = s[:len(s)-1] + } + n, err := parseInt(s) + if err != nil { + return err + } + if star { + if n == 0 { + return &EpochError{Message: epochZeroStar} + } + e.Read = []uint32{n - 1, n} + } else { + e.Read = []uint32{n} + } + e.Write = []uint32{n} + + return nil +} + +func (e *Epoch) fromStructured(structured structuredEpoch) error { + if structured.Read == nil { + if structured.Write == nil { + structured.Write = []uint32{0} + } + structured.Read = structured.Write + } else if len(structured.Read) == 0 { + // this means they explicitly set it to []. Bad they! + return &EpochError{Message: emptyEpochList} + } + if structured.Write == nil { + structured.Write = structured.Read[len(structured.Read)-1:] + } else if len(structured.Write) == 0 { + return &EpochError{Message: emptyEpochList} + } + + p := &Epoch{Read: structured.Read, Write: structured.Write} + if err := p.Validate(); err != nil { + return err + } + + *e = *p + + return nil +} + +func (e *Epoch) UnmarshalJSON(bs []byte) error { + return e.UnmarshalYAML(func(v interface{}) error { + return json.Unmarshal(bs, &v) + }) +} + +func (e *Epoch) UnmarshalYAML(unmarshal func(interface{}) error) error { + var shortEpoch string + if err := unmarshal(&shortEpoch); err == nil { + return e.fromString(shortEpoch) + } + var structured structuredEpoch + if err := unmarshal(&structured); err != nil { + return err + } + + return e.fromStructured(structured) +} + +// Validate checks that the epoch makes sense. +func (e *Epoch) Validate() error { + if e == nil || (e.Read == nil && e.Write == nil) { + // (*Epoch)(nil) and &Epoch{} are valid epochs, equivalent to "0" + return nil + } + if len(e.Read) == 0 || len(e.Write) == 0 { + return &EpochError{Message: emptyEpochList} + } + if len(e.Read) > 10 || len(e.Write) > 10 { + return &EpochError{Message: epochListJustRidiculouslyLong} + } + if !sort.IsSorted(uint32slice(e.Read)) || !sort.IsSorted(uint32slice(e.Write)) { + return &EpochError{Message: epochListNotSorted} + } + + // O(𝑚𝑛) instead of O(𝑚log𝑛) for the binary search we could do, but + // 𝑚 and 𝑛 <10 per above, so the simple solution is good enough (and if + // that alone makes you nervous, know that it is ~2× faster in the + // worst case; bisect starts being faster at ~50 entries). + for _, r := range e.Read { + for _, w := range e.Write { + if r == w { + return nil + } + } + } + return &EpochError{Message: noEpochIntersection} +} + +func (e *Epoch) simplify() interface{} { + if e == nil || (e.Read == nil && e.Write == nil) { + return "0" + } + if len(e.Write) == 1 && len(e.Read) == 1 && e.Read[0] == e.Write[0] { + return strconv.FormatUint(uint64(e.Read[0]), 10) + } + if len(e.Write) == 1 && len(e.Read) == 2 && e.Read[0]+1 == e.Read[1] && e.Read[1] == e.Write[0] { + return strconv.FormatUint(uint64(e.Read[1]), 10) + "*" + } + return &structuredEpoch{Read: e.Read, Write: e.Write} +} + +func (e *Epoch) MarshalJSON() ([]byte, error) { + return json.Marshal(e.simplify()) +} + +func (Epoch) MarshalYAML() (interface{}, error) { + panic("unexpected attempt to marshal an Epoch to YAML") +} + +func (e *Epoch) String() string { + i := e.simplify() + if s, ok := i.(string); ok { + return s + } + + buf, err := json.Marshal(i) + if err != nil { + // can this happen? + logger.Noticef("trying to marshal %#v, simplified to %#v, got %v", e, i, err) + return "-1" + } + return string(buf) +} + +// EpochError tracks the details of a failed epoch parse or validation. +type EpochError struct { + Message string +} + +func (e EpochError) Error() string { + return e.Message +} + +const ( + epochZeroStar = "0* is an invalid epoch" + hugeEpochNumber = "epoch numbers must be less than 2³², but got %q" + badEpochNumber = "epoch numbers must be base 10 with no zero padding, but got %q" + badEpochList = "epoch read/write attributes must be lists of epoch numbers" + emptyEpochList = "epoch list cannot be explicitly empty" + epochListNotSorted = "epoch list must be in ascending order" + epochListJustRidiculouslyLong = "epoch list must not have more than 10 entries" + noEpochIntersection = "epoch read and write lists must have a non-empty intersection" +) + +func parseInt(s string) (uint32, error) { + if !(len(s) > 1 && s[0] == '0') { + u, err := strconv.ParseUint(s, 10, 32) + if err == nil { + return uint32(u), nil + } + if e, ok := err.(*strconv.NumError); ok { + if e.Err == strconv.ErrRange { + return 0, &EpochError{ + Message: fmt.Sprintf(hugeEpochNumber, s), + } + } + } + } + return 0, &EpochError{ + Message: fmt.Sprintf(badEpochNumber, s), + } +} + +type uint32slice []uint32 + +func (ns uint32slice) Len() int { return len(ns) } +func (ns uint32slice) Less(i, j int) bool { return ns[i] < ns[j] } +func (ns uint32slice) Swap(i, j int) { panic("no reordering") } + +func (z *uint32slice) UnmarshalYAML(unmarshal func(interface{}) error) error { + var ss []string + if err := unmarshal(&ss); err != nil { + return &EpochError{Message: badEpochList} + } + x := make([]uint32, len(ss)) + for i, s := range ss { + n, err := parseInt(s) + if err != nil { + return err + } + x[i] = n + } + *z = x + return nil +} + +func (z *uint32slice) UnmarshalJSON(bs []byte) error { + var ss []json.RawMessage + if err := json.Unmarshal(bs, &ss); err != nil { + return &EpochError{Message: badEpochList} + } + x := make([]uint32, len(ss)) + for i, s := range ss { + n, err := parseInt(string(s)) + if err != nil { + return err + } + x[i] = n + } + *z = x + return nil +} + +type structuredEpoch struct { + Read uint32slice `json:"read"` + Write uint32slice `json:"write"` +} diff -Nru snapd-2.28.5/snap/epoch_test.go snapd-2.29.3/snap/epoch_test.go --- snapd-2.28.5/snap/epoch_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/snap/epoch_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,231 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package snap_test + +import ( + "encoding/json" + "github.com/snapcore/snapd/snap" + + "gopkg.in/check.v1" + "gopkg.in/yaml.v2" +) + +type epochSuite struct{} + +var _ = check.Suite(&epochSuite{}) + +var ( + // some duplication here maybe + epochZeroStar = `0\* is an invalid epoch` + hugeEpochNumber = `epoch numbers must be less than 2³², but got .*` + badEpochNumber = `epoch numbers must be base 10 with no zero padding, but got .*` + badEpochList = "epoch read/write attributes must be lists of epoch numbers" + emptyEpochList = "epoch list cannot be explicitly empty" + epochListNotSorted = "epoch list must be in ascending order" + epochListJustRidiculouslyLong = "epoch list must not have more than 10 entries" + noEpochIntersection = "epoch read and write lists must have a non-empty intersection" +) + +func (s epochSuite) TestBadEpochs(c *check.C) { + type Tt struct { + s string + e string + y int + } + + tests := []Tt{ + {s: `"rubbish"`, e: badEpochNumber}, // SISO + {s: `0xA`, e: badEpochNumber, y: 1}, // no hex + {s: `"0xA"`, e: badEpochNumber}, // + {s: `001`, e: badEpochNumber, y: 1}, // no octal, in fact no zero prefixes at all + {s: `"001"`, e: badEpochNumber}, // + {s: `{"read": 5}`, e: badEpochList}, // when split, must be list + {s: `{"write": 5}`, e: badEpochList}, // + {s: `{"read": "5"}`, e: badEpochList}, // + {s: `{"write": "5"}`, e: badEpochList}, // + {s: `{"read": "1*"}`, e: badEpochList}, // what + {s: `{"read": [-1]}`, e: badEpochNumber}, // negative not allowed + {s: `{"write": [-1]}`, e: badEpochNumber}, // + {s: `{"read": ["-1"]}`, e: badEpochNumber}, // + {s: `{"write": ["-1"]}`, e: badEpochNumber}, // + {s: `{"read": ["yes"]}`, e: badEpochNumber}, // must be numbers + {s: `{"write": ["yes"]}`, e: badEpochNumber}, // + {s: `{"read": ["Ⅰ","Ⅱ"]}`, e: badEpochNumber}, // not roman numerals you idiot + {s: `{"read": [0xA]}`, e: badEpochNumber, y: 1}, // + {s: `{"read": [010]}`, e: badEpochNumber, y: 1}, // + {s: `{"read": [9999999999]}`, e: hugeEpochNumber}, // you done yet? + {s: `"0*"`, e: epochZeroStar}, // 0* means nothing + {s: `"42**"`, e: badEpochNumber}, // N** is dead + {s: `{"read": []}`, e: emptyEpochList}, // explicitly empty is bad + {s: `{"write": []}`, e: emptyEpochList}, // + {s: `{"read": [1,2,4,3]}`, e: epochListNotSorted}, // must be ordered + {s: `{"write": [4,3,2,1]}`, e: epochListNotSorted}, // ...in ascending order + {s: `{"read": [0], "write": [1]}`, e: noEpochIntersection}, // must have at least one in common + {s: `{"read": [0,1,2,3,4,5,6,7,8,9,10], + "write": [0,1,2,3,4,5,6,7,8,9,10]}`, e: epochListJustRidiculouslyLong}, // must have <10 elements + } + + for _, test := range tests { + var v snap.Epoch + err := yaml.Unmarshal([]byte(test.s), &v) + c.Check(err, check.ErrorMatches, test.e, check.Commentf("YAML: %#q", test.s)) + + if test.y == 1 { + continue + } + err = json.Unmarshal([]byte(test.s), &v) + c.Check(err, check.ErrorMatches, test.e, check.Commentf("JSON: %#q", test.s)) + } +} + +func (s epochSuite) TestGoodEpochs(c *check.C) { + type Tt struct { + s string + e snap.Epoch + y int + } + + tests := []Tt{ + {s: `0`, e: snap.Epoch{Read: []uint32{0}, Write: []uint32{0}}, y: 1}, + {s: `""`, e: snap.Epoch{Read: []uint32{0}, Write: []uint32{0}}}, + {s: `"0"`, e: snap.Epoch{Read: []uint32{0}, Write: []uint32{0}}}, + {s: `{}`, e: snap.Epoch{Read: []uint32{0}, Write: []uint32{0}}}, + {s: `"2*"`, e: snap.Epoch{Read: []uint32{1, 2}, Write: []uint32{2}}}, + {s: `{"read": [2]}`, e: snap.Epoch{Read: []uint32{2}, Write: []uint32{2}}}, + {s: `{"read": [1, 2]}`, e: snap.Epoch{Read: []uint32{1, 2}, Write: []uint32{2}}}, + {s: `{"write": [2]}`, e: snap.Epoch{Read: []uint32{2}, Write: []uint32{2}}}, + {s: `{"write": [1, 2]}`, e: snap.Epoch{Read: []uint32{1, 2}, Write: []uint32{1, 2}}}, + {s: `{"read": [2,4,8], "write": [2,3,5]}`, e: snap.Epoch{Read: []uint32{2, 4, 8}, Write: []uint32{2, 3, 5}}}, + } + + for _, test := range tests { + var v snap.Epoch + err := yaml.Unmarshal([]byte(test.s), &v) + c.Check(err, check.IsNil, check.Commentf("YAML: %s", test.s)) + c.Check(v, check.DeepEquals, test.e) + + if test.y > 0 { + continue + } + + err = json.Unmarshal([]byte(test.s), &v) + c.Check(err, check.IsNil, check.Commentf("JSON: %s", test.s)) + c.Check(v, check.DeepEquals, test.e) + } +} + +func (s *epochSuite) TestEpochValidate(c *check.C) { + validEpochs := []*snap.Epoch{ + nil, + {}, + {Read: []uint32{0}, Write: []uint32{0}}, + {Read: []uint32{0, 1}, Write: []uint32{1}}, + {Read: []uint32{1}, Write: []uint32{1}}, + {Read: []uint32{399, 400}, Write: []uint32{400}}, + {Read: []uint32{1, 2, 3}, Write: []uint32{1, 2, 3}}, + } + for _, epoch := range validEpochs { + err := epoch.Validate() + c.Check(err, check.IsNil, check.Commentf("%s", epoch)) + } + invalidEpochs := []struct { + epoch *snap.Epoch + err string + }{ + {epoch: &snap.Epoch{Read: []uint32{}}, err: emptyEpochList}, + {epoch: &snap.Epoch{Write: []uint32{}}, err: emptyEpochList}, + {epoch: &snap.Epoch{Read: []uint32{}, Write: []uint32{}}, err: emptyEpochList}, + {epoch: &snap.Epoch{Read: []uint32{1}, Write: []uint32{2}}, err: noEpochIntersection}, + {epoch: &snap.Epoch{Read: []uint32{1, 3, 5}, Write: []uint32{2, 4, 6}}, err: noEpochIntersection}, + {epoch: &snap.Epoch{Read: []uint32{1, 2, 3}, Write: []uint32{3, 2, 1}}, err: epochListNotSorted}, + {epoch: &snap.Epoch{Read: []uint32{3, 2, 1}, Write: []uint32{1, 2, 3}}, err: epochListNotSorted}, + {epoch: &snap.Epoch{Read: []uint32{3, 2, 1}, Write: []uint32{3, 2, 1}}, err: epochListNotSorted}, + {epoch: &snap.Epoch{ + Read: []uint32{0}, + Write: []uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + }, err: epochListJustRidiculouslyLong}, + {epoch: &snap.Epoch{ + Read: []uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + Write: []uint32{0}, + }, err: epochListJustRidiculouslyLong}, + {epoch: &snap.Epoch{ + Read: []uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + Write: []uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + }, err: epochListJustRidiculouslyLong}, + } + for _, test := range invalidEpochs { + err := test.epoch.Validate() + c.Check(err, check.ErrorMatches, test.err, check.Commentf("%s", test.epoch)) + } +} + +func (s *epochSuite) TestEpochString(c *check.C) { + tests := []struct { + e *snap.Epoch + s string + }{ + {e: nil, s: "0"}, + {e: &snap.Epoch{}, s: "0"}, + {e: &snap.Epoch{Read: []uint32{0}, Write: []uint32{0}}, s: "0"}, + {e: &snap.Epoch{Read: []uint32{0, 1}, Write: []uint32{1}}, s: "1*"}, + {e: &snap.Epoch{Read: []uint32{1}, Write: []uint32{1}}, s: "1"}, + {e: &snap.Epoch{Read: []uint32{399, 400}, Write: []uint32{400}}, s: "400*"}, + {e: &snap.Epoch{Read: []uint32{1, 2, 3}, Write: []uint32{1, 2, 3}}, s: `{"read":[1,2,3],"write":[1,2,3]}`}, + } + for _, test := range tests { + c.Check(test.e.String(), check.Equals, test.s, check.Commentf(test.s)) + } +} + +func (s *epochSuite) TestEpochMarshal(c *check.C) { + tests := []struct { + e *snap.Epoch + s string + }{ + // {e: nil, s: `"0"`}, + {e: &snap.Epoch{}, s: `"0"`}, + {e: &snap.Epoch{Read: []uint32{0}, Write: []uint32{0}}, s: `"0"`}, + {e: &snap.Epoch{Read: []uint32{0, 1}, Write: []uint32{1}}, s: `"1*"`}, + {e: &snap.Epoch{Read: []uint32{1}, Write: []uint32{1}}, s: `"1"`}, + {e: &snap.Epoch{Read: []uint32{399, 400}, Write: []uint32{400}}, s: `"400*"`}, + {e: &snap.Epoch{Read: []uint32{1, 2, 3}, Write: []uint32{1, 2, 3}}, s: `{"read":[1,2,3],"write":[1,2,3]}`}, + } + for _, test := range tests { + bs, err := json.Marshal(test.e) + c.Assert(err, check.IsNil) + c.Check(string(bs), check.Equals, test.s, check.Commentf(test.s)) + } +} + +func (s *epochSuite) TestE(c *check.C) { + tests := []struct { + e *snap.Epoch + s string + }{ + {s: "0", e: &snap.Epoch{Read: []uint32{0}, Write: []uint32{0}}}, + {s: "1", e: &snap.Epoch{Read: []uint32{1}, Write: []uint32{1}}}, + {s: "1*", e: &snap.Epoch{Read: []uint32{0, 1}, Write: []uint32{1}}}, + {s: "400*", e: &snap.Epoch{Read: []uint32{399, 400}, Write: []uint32{400}}}, + } + for _, test := range tests { + c.Check(snap.E(test.s), check.DeepEquals, test.e, check.Commentf(test.s)) + c.Check(test.e.String(), check.Equals, test.s, check.Commentf(test.s)) + } +} diff -Nru snapd-2.28.5/snap/info.go snapd-2.29.3/snap/info.go --- snapd-2.28.5/snap/info.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/snap/info.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,6 +20,7 @@ package snap import ( + "bytes" "fmt" "io/ioutil" "os" @@ -49,9 +50,6 @@ // DataDir returns the data directory of the snap. DataDir() string - // HomeDirBase returns the user-specific home directory base of the snap. - HomeDirBase(home string) string - // UserDataDir returns the per user data directory of the snap. UserDataDir(home string) string @@ -156,7 +154,7 @@ LicenseAgreement string LicenseVersion string License string - Epoch string + Epoch Epoch Base string Confinement ConfinementType Apps map[string]*AppInfo @@ -165,6 +163,9 @@ Plugs map[string]*PlugInfo Slots map[string]*SlotInfo + // Plugs or slots with issues (they are not included in Plugs or Slots) + BadInterfaces map[string]string // slot or plug => message + // The information in all the remaining fields is not sourced from the snap blob itself. SideInfo @@ -212,7 +213,7 @@ Confinement ConfinementType `json:"confinement"` Version string `json:"version"` Channel string `json:"channel"` - Epoch string `json:"epoch"` + Epoch Epoch `json:"epoch"` Size int64 `json:"size"` } @@ -273,11 +274,6 @@ return filepath.Join(home, "snap", s.Name(), s.Revision.String()) } -// HomeDirBase returns the user-specific home directory base of the snap. -func (s *Info) HomeDirBase(home string) string { - return filepath.Join(home, "snap", s.Name()) -} - // UserCommonDataDir returns the user-specific data directory common across revision of the snap. func (s *Info) UserCommonDataDir(home string) string { return filepath.Join(home, "snap", s.Name(), "common") @@ -331,6 +327,32 @@ return svcs } +func BadInterfacesSummary(snapInfo *Info) string { + inverted := make(map[string][]string) + for name, reason := range snapInfo.BadInterfaces { + inverted[reason] = append(inverted[reason], name) + } + var buf bytes.Buffer + fmt.Fprintf(&buf, "snap %q has bad plugs or slots: ", snapInfo.Name()) + reasons := make([]string, 0, len(inverted)) + for reason := range inverted { + reasons = append(reasons, reason) + } + sort.Strings(reasons) + for _, reason := range reasons { + names := inverted[reason] + sort.Strings(names) + for i, name := range names { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(name) + } + fmt.Fprintf(&buf, " (%s); ", reason) + } + return strings.TrimSuffix(buf.String(), "; ") +} + // DownloadInfo contains the information to download a snap. // It can be marshalled. type DownloadInfo struct { @@ -465,26 +487,12 @@ // WrapperPath returns the path to wrapper invoking the app binary. func (app *AppInfo) WrapperPath() string { - var binName string - if app.Name == app.Snap.Name() { - binName = filepath.Base(app.Name) - } else { - binName = fmt.Sprintf("%s.%s", app.Snap.Name(), filepath.Base(app.Name)) - } - - return filepath.Join(dirs.SnapBinariesDir, binName) + return filepath.Join(dirs.SnapBinariesDir, JoinSnapApp(app.Snap.Name(), app.Name)) } // CompleterPath returns the path to the completer snippet for the app binary. func (app *AppInfo) CompleterPath() string { - var binName string - if app.Name == app.Snap.Name() { - binName = filepath.Base(app.Name) - } else { - binName = fmt.Sprintf("%s.%s", app.Snap.Name(), filepath.Base(app.Name)) - } - - return filepath.Join(dirs.CompletersDir, binName) + return filepath.Join(dirs.CompletersDir, JoinSnapApp(app.Snap.Name(), app.Name)) } func (app *AppInfo) launcherCommand(command string) string { @@ -494,7 +502,7 @@ if app.Name == app.Snap.Name() { return fmt.Sprintf("/usr/bin/snap run%s %s", command, app.Name) } - return fmt.Sprintf("/usr/bin/snap run%s %s.%s", command, app.Snap.Name(), filepath.Base(app.Name)) + return fmt.Sprintf("/usr/bin/snap run%s %s.%s", command, app.Snap.Name(), app.Name) } // LauncherCommand returns the launcher command line to use when invoking the app binary. @@ -590,6 +598,16 @@ return fmt.Sprintf("cannot find installed snap %q at revision %s", e.Snap, e.Revision) } +func MockSanitizePlugsSlots(f func(snapInfo *Info)) (restore func()) { + old := SanitizePlugsSlots + SanitizePlugsSlots = f + return func() { SanitizePlugsSlots = old } +} + +var SanitizePlugsSlots = func(snapInfo *Info) { + panic("SanitizePlugsSlots function not set") +} + // ReadInfo reads the snap information for the installed snap with the given name and given side-info. func ReadInfo(name string, si *SideInfo) (*Info, error) { snapYamlFn := filepath.Join(MountDir(name, si.Revision), "meta", "snap.yaml") @@ -617,6 +635,8 @@ return nil, err } + SanitizePlugsSlots(info) + return info, nil } diff -Nru snapd-2.28.5/snap/info_snap_yaml.go snapd-2.29.3/snap/info_snap_yaml.go --- snapd-2.28.5/snap/info_snap_yaml.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/snap/info_snap_yaml.go 2017-10-23 06:17:27.000000000 +0000 @@ -44,7 +44,7 @@ License string `yaml:"license,omitempty"` LicenseAgreement string `yaml:"license-agreement,omitempty"` LicenseVersion string `yaml:"license-version,omitempty"` - Epoch string `yaml:"epoch,omitempty"` + Epoch Epoch `yaml:"epoch,omitempty"` Base string `yaml:"base,omitempty"` Confinement ConfinementType `yaml:"confinement,omitempty"` Environment strutil.OrderedMap `yaml:"environment,omitempty"` @@ -164,6 +164,8 @@ // Rename specific plugs on the core snap. snap.renameClashingCorePlugs() + snap.BadInterfaces = make(map[string]string) + // FIXME: validation of the fields return snap, nil } @@ -180,10 +182,6 @@ if y.Type != "" { typ = y.Type } - epoch := "0" - if y.Epoch != "" { - epoch = y.Epoch - } confinement := StrictConfinement if y.Confinement != "" { confinement = y.Confinement @@ -202,7 +200,7 @@ License: y.License, LicenseAgreement: y.LicenseAgreement, LicenseVersion: y.LicenseVersion, - Epoch: epoch, + Epoch: y.Epoch, Confinement: confinement, Base: y.Base, Apps: make(map[string]*AppInfo), diff -Nru snapd-2.28.5/snap/info_snap_yaml_test.go snapd-2.29.3/snap/info_snap_yaml_test.go --- snapd-2.28.5/snap/info_snap_yaml_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/snap/info_snap_yaml_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -1089,7 +1089,7 @@ c.Check(info.Name(), Equals, "foo") c.Check(info.Version, Equals, "1.2") c.Check(info.Type, Equals, snap.TypeApp) - c.Check(info.Epoch, Equals, "1*") + c.Check(info.Epoch.String(), Equals, "1*") c.Check(info.Confinement, Equals, snap.DevModeConfinement) c.Check(info.Title(), Equals, "Foo") c.Check(info.Summary(), Equals, "foo app") @@ -1229,7 +1229,7 @@ `) info, err := snap.InfoFromSnapYaml(y) c.Assert(err, IsNil) - c.Assert(info.Epoch, Equals, "0") + c.Assert(info.Epoch.String(), Equals, "0") } func (s *YamlSuite) TestSnapYamlConfinementDefault(c *C) { diff -Nru snapd-2.28.5/snap/info_test.go snapd-2.29.3/snap/info_test.go --- snapd-2.28.5/snap/info_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/snap/info_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -33,23 +33,43 @@ "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/snap/snaptest" "github.com/snapcore/snapd/snap/squashfs" + "github.com/snapcore/snapd/testutil" ) type infoSuite struct { - restore func() + testutil.BaseTest } +type infoSimpleSuite struct{} + var _ = Suite(&infoSuite{}) +var _ = Suite(&infoSimpleSuite{}) + +func (s *infoSimpleSuite) SetUpTest(c *C) { + dirs.SetRootDir(c.MkDir()) +} + +func (s *infoSimpleSuite) TearDownTest(c *C) { + dirs.SetRootDir("") +} + +func (s *infoSimpleSuite) TestReadInfoPanicsIfSanitizeUnset(c *C) { + si := &snap.SideInfo{Revision: snap.R(1)} + snaptest.MockSnap(c, sampleYaml, sampleContents, si) + c.Assert(func() { snap.ReadInfo("sample", si) }, Panics, `SanitizePlugsSlots function not set`) +} func (s *infoSuite) SetUpTest(c *C) { + s.BaseTest.SetUpTest(c) dirs.SetRootDir(c.MkDir()) hookType := snap.NewHookType(regexp.MustCompile(".*")) - s.restore = snap.MockSupportedHookTypes([]*snap.HookType{hookType}) + s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) + s.BaseTest.AddCleanup(snap.MockSupportedHookTypes([]*snap.HookType{hookType})) } func (s *infoSuite) TearDownTest(c *C) { + s.BaseTest.TearDownTest(c) dirs.SetRootDir("") - s.restore() } func (s *infoSuite) TestSideInfoOverrides(c *C) { @@ -258,7 +278,7 @@ c.Check(info.Version, Equals, "1.0") c.Check(info.Type, Equals, snap.TypeApp) c.Check(info.Revision, Equals, snap.R(0)) - c.Check(info.Epoch, Equals, "1*") + c.Check(info.Epoch.String(), Equals, "1*") c.Check(info.Confinement, Equals, snap.DevModeConfinement) c.Check(info.NeedsDevMode(), Equals, true) c.Check(info.NeedsClassic(), Equals, false) @@ -300,7 +320,7 @@ c.Check(info.Version, Equals, "1.0") c.Check(info.Type, Equals, snap.TypeApp) c.Check(info.Revision, Equals, snap.R(0)) - c.Check(info.Epoch, Equals, "0") // Defaults to 0 + c.Check(info.Epoch.String(), Equals, "0") // Defaults to 0 c.Check(info.Confinement, Equals, snap.StrictConfinement) c.Check(info.NeedsDevMode(), Equals, false) } @@ -443,12 +463,12 @@ } } -func ExampleSpltiSnapApp() { +func ExampleSplitSnapApp() { fmt.Println(snap.SplitSnapApp("hello-world.env")) // Output: hello-world env } -func ExampleSpltiSnapAppShort() { +func ExampleSplitSnapAppShort() { fmt.Println(snap.SplitSnapApp("hello-world")) // Output: hello-world hello-world } @@ -531,7 +551,7 @@ func (s *infoSuite) TestReadInfoInvalidImplicitHook(c *C) { hookType := snap.NewHookType(regexp.MustCompile("foo")) - s.restore = snap.MockSupportedHookTypes([]*snap.HookType{hookType}) + s.BaseTest.AddCleanup(snap.MockSupportedHookTypes([]*snap.HookType{hookType})) yaml := `name: foo version: 1.0` @@ -604,7 +624,6 @@ c.Check(info.HooksDir(), Equals, fmt.Sprintf("%s/name/1/meta/hooks", dirs.SnapMountDir)) c.Check(info.DataDir(), Equals, "/var/snap/name/1") c.Check(info.UserDataDir("/home/bob"), Equals, "/home/bob/snap/name/1") - c.Check(info.HomeDirBase("/home/bob"), Equals, "/home/bob/snap/name") c.Check(info.UserCommonDataDir("/home/bob"), Equals, "/home/bob/snap/name/common") c.Check(info.CommonDataDir(), Equals, "/var/snap/name/common") c.Check(info.UserXdgRuntimeDir(12345), Equals, "/run/user/12345/snap.name") diff -Nru snapd-2.28.5/snap/pack/export_test.go snapd-2.29.3/snap/pack/export_test.go --- snapd-2.28.5/snap/pack/export_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/snap/pack/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,26 @@ +// -*- 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 . + * + */ + +package pack + +var ( + CopyToBuildDir = copyToBuildDir + ShouldExcludeDynamic = shouldExcludeDynamic + DebArchitecture = debArchitecture +) diff -Nru snapd-2.28.5/snap/pack/pack.go snapd-2.29.3/snap/pack/pack.go --- snapd-2.28.5/snap/pack/pack.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/snap/pack/pack.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,268 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2014-2017 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 . + * + */ + +package pack + +import ( + "bufio" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + "syscall" + + "github.com/snapcore/snapd/osutil" + "github.com/snapcore/snapd/snap" + "github.com/snapcore/snapd/snap/squashfs" +) + +// from click's click.build.ClickBuilderBase, and there from +// @Dpkg::Source::Package::tar_ignore_default_pattern; +// changed to regexps from globs for sanity (hah) +// +// Please resist the temptation of optimizing the regexp by grouping +// things by hand. People will find it unreadable enough as it is. +var shouldExcludeDefault = regexp.MustCompile(strings.Join([]string{ + `\.snap$`, // added + `\.click$`, + `^\..*\.sw.$`, + `~$`, + `^,,`, + `^\.[#~]`, + `^\.arch-ids$`, + `^\.arch-inventory$`, + `^\.bzr$`, + `^\.bzr-builddeb$`, + `^\.bzr\.backup$`, + `^\.bzr\.tags$`, + `^\.bzrignore$`, + `^\.cvsignore$`, + `^\.git$`, + `^\.gitattributes$`, + `^\.gitignore$`, + `^\.gitmodules$`, + `^\.hg$`, + `^\.hgignore$`, + `^\.hgsigs$`, + `^\.hgtags$`, + `^\.shelf$`, + `^\.svn$`, + `^CVS$`, + `^DEADJOE$`, + `^RCS$`, + `^_MTN$`, + `^_darcs$`, + `^{arch}$`, + `^\.snapignore$`, +}, "|")).MatchString + +// fake static function variables +type keep struct { + basedir string + exclude func(string) bool +} + +func (k *keep) shouldExclude(basedir string, file string) bool { + if basedir == k.basedir { + if k.exclude == nil { + return false + } + + return k.exclude(file) + } + + k.basedir = basedir + k.exclude = nil + + snapignore, err := os.Open(filepath.Join(basedir, ".snapignore")) + if err != nil { + return false + } + + scanner := bufio.NewScanner(snapignore) + var lines []string + for scanner.Scan() { + line := scanner.Text() + if _, err := regexp.Compile(line); err != nil { + // not a regexp + line = regexp.QuoteMeta(line) + } + lines = append(lines, line) + } + + fullRegex := strings.Join(lines, "|") + exclude, err := regexp.Compile(fullRegex) + if err == nil { + k.exclude = exclude.MatchString + + return k.exclude(file) + } + + // can't happen; can't even find a way to trigger it in testing. + panic(fmt.Sprintf("internal error: composition of valid regexps is invalid?!? Please report this bug: %#v", fullRegex)) +} + +var shouldExcludeDynamic = new(keep).shouldExclude + +func shouldExclude(basedir string, file string) bool { + return shouldExcludeDefault(file) || shouldExcludeDynamic(basedir, file) +} + +// small helper that return the architecture or "multi" if its multiple arches +func debArchitecture(info *snap.Info) string { + switch len(info.Architectures) { + case 0: + return "all" + case 1: + return info.Architectures[0] + default: + return "multi" + } +} + +func copyToBuildDir(sourceDir, buildDir string) error { + sourceDir, err := filepath.Abs(sourceDir) + if err != nil { + return err + } + + err = os.Remove(buildDir) + if err != nil && !os.IsNotExist(err) { + // this shouldn't happen, but. + return err + } + + // no umask here so that we get the permissions correct + oldUmask := syscall.Umask(0) + defer syscall.Umask(oldUmask) + + return filepath.Walk(sourceDir, func(path string, info os.FileInfo, errin error) (err error) { + if errin != nil { + return errin + } + + relpath := path[len(sourceDir):] + if relpath == "/DEBIAN" || shouldExclude(sourceDir, filepath.Base(path)) { + if info.IsDir() { + return filepath.SkipDir + } + return nil + } + + dest := filepath.Join(buildDir, relpath) + + // handle dirs + if info.IsDir() { + if err := os.Mkdir(dest, info.Mode()); err != nil { + return err + } + // ensure that permissions are preserved + uid := int(info.Sys().(*syscall.Stat_t).Uid) + gid := int(info.Sys().(*syscall.Stat_t).Gid) + return os.Chown(dest, uid, gid) + } + + // handle char/block devices + if osutil.IsDevice(info.Mode()) { + return osutil.CopySpecialFile(path, dest) + } + + if (info.Mode() & os.ModeSymlink) != 0 { + target, err := os.Readlink(path) + if err != nil { + return err + } + return os.Symlink(target, dest) + } + + // fail if its unsupported + if !info.Mode().IsRegular() { + return fmt.Errorf("cannot handle type of file %s", path) + } + + // it's a file. Maybe we can link it? + if os.Link(path, dest) == nil { + // whee + return nil + } + // sigh. ok, copy it is. + return osutil.CopyFile(path, dest, osutil.CopyFlagDefault) + }) +} + +func prepare(sourceDir, targetDir, buildDir string) (snapName string, err error) { + // ensure we have valid content + yaml, err := ioutil.ReadFile(filepath.Join(sourceDir, "meta", "snap.yaml")) + if err != nil { + return "", err + } + + info, err := snap.InfoFromSnapYaml(yaml) + if err != nil { + return "", err + } + + err = snap.Validate(info) + if err != nil { + return "", err + } + + if err := copyToBuildDir(sourceDir, buildDir); err != nil { + return "", err + } + + // build the package + snapName = fmt.Sprintf("%s_%s_%v.snap", info.Name(), info.Version, debArchitecture(info)) + + if targetDir != "" { + snapName = filepath.Join(targetDir, snapName) + if _, err := os.Stat(targetDir); os.IsNotExist(err) { + if err := os.MkdirAll(targetDir, 0755); err != nil { + return "", err + } + } + } + + return snapName, nil +} + +// Snap the given sourceDirectory and return the generated +// snap file +func Snap(sourceDir, targetDir string) (string, error) { + // create build dir + buildDir, err := ioutil.TempDir("", "snappy-build-") + if err != nil { + return "", err + } + defer os.RemoveAll(buildDir) + + snapName, err := prepare(sourceDir, targetDir, buildDir) + if err != nil { + return "", err + } + + d := squashfs.New(snapName) + if err = d.Build(buildDir); err != nil { + return "", err + } + + return snapName, nil +} diff -Nru snapd-2.28.5/snap/pack/pack_test.go snapd-2.29.3/snap/pack/pack_test.go --- snapd-2.28.5/snap/pack/pack_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/snap/pack/pack_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,286 @@ +// -*- 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 . + * + */ + +package pack_test + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + "syscall" + "testing" + + "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/snap" + "github.com/snapcore/snapd/snap/pack" + "github.com/snapcore/snapd/testutil" + + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { TestingT(t) } + +type packSuite struct { + testutil.BaseTest +} + +var _ = Suite(&packSuite{}) + +func (s *packSuite) SetUpTest(c *C) { + s.BaseTest.SetUpTest(c) + + // chdir into a tempdir + pwd, err := os.Getwd() + c.Assert(err, IsNil) + s.AddCleanup(func() { os.Chdir(pwd) }) + err = os.Chdir(c.MkDir()) + c.Assert(err, IsNil) + + // use fake root + dirs.SetRootDir(c.MkDir()) +} + +func makeExampleSnapSourceDir(c *C, snapYamlContent string) string { + tempdir := c.MkDir() + + // use meta/snap.yaml + metaDir := filepath.Join(tempdir, "meta") + err := os.Mkdir(metaDir, 0755) + c.Assert(err, IsNil) + err = ioutil.WriteFile(filepath.Join(metaDir, "snap.yaml"), []byte(snapYamlContent), 0644) + c.Assert(err, IsNil) + + const helloBinContent = `#!/bin/sh +printf "hello world" +` + + // an example binary + binDir := filepath.Join(tempdir, "bin") + err = os.Mkdir(binDir, 0755) + c.Assert(err, IsNil) + err = ioutil.WriteFile(filepath.Join(binDir, "hello-world"), []byte(helloBinContent), 0755) + c.Assert(err, IsNil) + + // unusual permissions for dir + tmpDir := filepath.Join(tempdir, "tmp") + err = os.Mkdir(tmpDir, 0755) + c.Assert(err, IsNil) + // avoid umask + err = os.Chmod(tmpDir, 01777) + c.Assert(err, IsNil) + + // and file + someFile := filepath.Join(tempdir, "file-with-perm") + err = ioutil.WriteFile(someFile, []byte(""), 0666) + c.Assert(err, IsNil) + err = os.Chmod(someFile, 0666) + c.Assert(err, IsNil) + + // an example symlink + err = os.Symlink("bin/hello-world", filepath.Join(tempdir, "symlink")) + c.Assert(err, IsNil) + + return tempdir +} + +func (s *packSuite) TestPackNoManifestFails(c *C) { + sourceDir := makeExampleSnapSourceDir(c, "") + c.Assert(os.Remove(filepath.Join(sourceDir, "meta", "snap.yaml")), IsNil) + _, err := pack.Snap(sourceDir, "") + c.Assert(err, NotNil) // XXX maybe make the error more explicit +} + +func (s *packSuite) TestCopyCopies(c *C) { + sourceDir := makeExampleSnapSourceDir(c, "name: hello") + // actually this'll be on /tmp so it'll be a link + target := c.MkDir() + c.Assert(pack.CopyToBuildDir(sourceDir, target), IsNil) + out, err := exec.Command("diff", "-qrN", sourceDir, target).Output() + c.Check(err, IsNil) + c.Check(out, DeepEquals, []byte{}) +} + +func (s *packSuite) TestCopyActuallyCopies(c *C) { + sourceDir := makeExampleSnapSourceDir(c, "name: hello") + + // hoping to get the non-linking behaviour via /dev/shm + target, err := ioutil.TempDir("/dev/shm", "copy") + // sbuild environments won't allow writing to /dev/shm, so its + // ok to skip there + if os.IsPermission(err) { + c.Skip("/dev/shm is not writable for us") + } + c.Assert(err, IsNil) + + c.Assert(pack.CopyToBuildDir(sourceDir, target), IsNil) + out, err := exec.Command("diff", "-qrN", sourceDir, target).Output() + c.Check(err, IsNil) + c.Check(out, DeepEquals, []byte{}) +} + +func (s *packSuite) TestCopyExcludesBackups(c *C) { + sourceDir := makeExampleSnapSourceDir(c, "name: hello") + target := c.MkDir() + // add a backup file + c.Assert(ioutil.WriteFile(filepath.Join(sourceDir, "foo~"), []byte("hi"), 0755), IsNil) + c.Assert(pack.CopyToBuildDir(sourceDir, target), IsNil) + cmd := exec.Command("diff", "-qr", sourceDir, target) + cmd.Env = append(cmd.Env, "LANG=C") + out, err := cmd.Output() + c.Check(err, NotNil) + c.Check(string(out), Matches, `(?m)Only in \S+: foo~`) +} + +func (s *packSuite) TestCopyExcludesTopLevelDEBIAN(c *C) { + sourceDir := makeExampleSnapSourceDir(c, "name: hello") + target := c.MkDir() + // add a toplevel DEBIAN + c.Assert(os.MkdirAll(filepath.Join(sourceDir, "DEBIAN", "foo"), 0755), IsNil) + // and a non-toplevel DEBIAN + c.Assert(os.MkdirAll(filepath.Join(sourceDir, "bar", "DEBIAN", "baz"), 0755), IsNil) + c.Assert(pack.CopyToBuildDir(sourceDir, target), IsNil) + cmd := exec.Command("diff", "-qr", sourceDir, target) + cmd.Env = append(cmd.Env, "LANG=C") + out, err := cmd.Output() + c.Check(err, NotNil) + c.Check(string(out), Matches, `(?m)Only in \S+: DEBIAN`) + // but *only one* DEBIAN is skipped + c.Check(strings.Count(string(out), "Only in"), Equals, 1) +} + +func (s *packSuite) TestCopyExcludesWholeDirs(c *C) { + sourceDir := makeExampleSnapSourceDir(c, "name: hello") + target := c.MkDir() + // add a file inside a skipped dir + c.Assert(os.Mkdir(filepath.Join(sourceDir, ".bzr"), 0755), IsNil) + c.Assert(ioutil.WriteFile(filepath.Join(sourceDir, ".bzr", "foo"), []byte("hi"), 0755), IsNil) + c.Assert(pack.CopyToBuildDir(sourceDir, target), IsNil) + out, _ := exec.Command("find", sourceDir).Output() + c.Check(string(out), Not(Equals), "") + cmd := exec.Command("diff", "-qr", sourceDir, target) + cmd.Env = append(cmd.Env, "LANG=C") + out, err := cmd.Output() + c.Check(err, NotNil) + c.Check(string(out), Matches, `(?m)Only in \S+: \.bzr`) +} + +func (s *packSuite) TestExcludeDynamicFalseIfNoSnapignore(c *C) { + basedir := c.MkDir() + c.Check(pack.ShouldExcludeDynamic(basedir, "foo"), Equals, false) +} + +func (s *packSuite) TestExcludeDynamicWorksIfSnapignore(c *C) { + basedir := c.MkDir() + c.Assert(ioutil.WriteFile(filepath.Join(basedir, ".snapignore"), []byte("foo\nb.r\n"), 0644), IsNil) + c.Check(pack.ShouldExcludeDynamic(basedir, "foo"), Equals, true) + c.Check(pack.ShouldExcludeDynamic(basedir, "bar"), Equals, true) + c.Check(pack.ShouldExcludeDynamic(basedir, "bzr"), Equals, true) + c.Check(pack.ShouldExcludeDynamic(basedir, "baz"), Equals, false) +} + +func (s *packSuite) TestExcludeDynamicWeirdRegexps(c *C) { + basedir := c.MkDir() + c.Assert(ioutil.WriteFile(filepath.Join(basedir, ".snapignore"), []byte("*hello\n"), 0644), IsNil) + // note "*hello" is not a valid regexp, so will be taken literally (not globbed!) + c.Check(pack.ShouldExcludeDynamic(basedir, "ahello"), Equals, false) + c.Check(pack.ShouldExcludeDynamic(basedir, "*hello"), Equals, true) +} + +func (s *packSuite) TestDebArchitecture(c *C) { + c.Check(pack.DebArchitecture(&snap.Info{Architectures: []string{"foo"}}), Equals, "foo") + c.Check(pack.DebArchitecture(&snap.Info{Architectures: []string{"foo", "bar"}}), Equals, "multi") + c.Check(pack.DebArchitecture(&snap.Info{Architectures: nil}), Equals, "all") +} + +func (s *packSuite) TestPackFailsForUnknownType(c *C) { + sourceDir := makeExampleSnapSourceDir(c, `name: hello +version: 1.0.1 +`) + err := syscall.Mkfifo(filepath.Join(sourceDir, "fifo"), 0644) + c.Assert(err, IsNil) + + _, err = pack.Snap(sourceDir, "") + c.Assert(err, ErrorMatches, "cannot handle type of file .*") +} + +func (s *packSuite) TestPackSimple(c *C) { + sourceDir := makeExampleSnapSourceDir(c, `name: hello +version: 1.0.1 +architectures: ["i386", "amd64"] +integration: + app: + apparmor-profile: meta/hello.apparmor +`) + + resultSnap, err := pack.Snap(sourceDir, "") + c.Assert(err, IsNil) + + // check that there is result + _, err = os.Stat(resultSnap) + c.Assert(err, IsNil) + c.Assert(resultSnap, Equals, "hello_1.0.1_multi.snap") + + // check that the content looks sane + output, err := exec.Command("unsquashfs", "-ll", "hello_1.0.1_multi.snap").CombinedOutput() + c.Assert(err, IsNil) + for _, needle := range []string{ + "meta/snap.yaml", + "bin/hello-world", + "symlink -> bin/hello-world", + } { + expr := fmt.Sprintf(`(?ms).*%s.*`, regexp.QuoteMeta(needle)) + c.Assert(string(output), Matches, expr) + } +} + +func (s *packSuite) TestPackSimpleOutputDir(c *C) { + sourceDir := makeExampleSnapSourceDir(c, `name: hello +version: 1.0.1 +architectures: ["i386", "amd64"] +integration: + app: + apparmor-profile: meta/hello.apparmor +`) + + outputDir := filepath.Join(c.MkDir(), "output") + snapOutput := filepath.Join(outputDir, "hello_1.0.1_multi.snap") + resultSnap, err := pack.Snap(sourceDir, outputDir) + c.Assert(err, IsNil) + + // check that there is result + _, err = os.Stat(resultSnap) + c.Assert(err, IsNil) + c.Assert(resultSnap, Equals, snapOutput) + + // check that the content looks sane + output, err := exec.Command("unsquashfs", "-ll", resultSnap).CombinedOutput() + c.Assert(err, IsNil) + for _, needle := range []string{ + "meta/snap.yaml", + "bin/hello-world", + "symlink -> bin/hello-world", + } { + expr := fmt.Sprintf(`(?ms).*%s.*`, regexp.QuoteMeta(needle)) + c.Assert(string(output), Matches, expr) + } +} diff -Nru snapd-2.28.5/snap/snapenv/snapenv.go snapd-2.29.3/snap/snapenv/snapenv.go --- snapd-2.28.5/snap/snapenv/snapenv.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/snap/snapenv/snapenv.go 2017-10-23 06:17:27.000000000 +0000 @@ -31,18 +31,33 @@ "github.com/snapcore/snapd/snap" ) +type preserveUnsafeEnvFlag int8 + +const ( + discardUnsafeFlag preserveUnsafeEnvFlag = iota + preserveUnsafeFlag +) + // ExecEnv returns the full environment that is required for // snap-{confine,exec}(like SNAP_{NAME,REVISION} etc are all set). // // It merges it with the existing os.Environ() and ensures the SNAP_* -// overrides the any pre-existing environment variables. +// overrides the any pre-existing environment variables. For a classic +// snap, environment variables that are usually stripped out by ld.so +// when starting a setuid process are renamed by prepending +// PreservedUnsafePrefix -- which snap-exec will remove, restoring the +// variables to their original names. // // With the extra parameter additional environment variables can be // supplied which will be set in the execution environment. func ExecEnv(info *snap.Info, extra map[string]string) []string { // merge environment and the snap environment, note that the // snap environment overrides pre-existing env entries - env := envMap(os.Environ()) + preserve := discardUnsafeFlag + if info.NeedsClassic() { + preserve = preserveUnsafeFlag + } + env := envMap(os.Environ(), preserve) snapEnv := snapEnv(info) for k, v := range snapEnv { env[k] = v @@ -113,16 +128,61 @@ return result } -// envMap creates a map from the given environment string list, e.g. the -// list returned from os.Environ() -func envMap(env []string) map[string]string { +// Environment variables glibc strips out when running a setuid binary. +// Taken from https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=sysdeps/generic/unsecvars.h;hb=HEAD +// TODO: use go generate to obtain this list at build time. +var unsafeEnv = map[string]bool{ + "GCONV_PATH": true, + "GETCONF_DIR": true, + "GLIBC_TUNABLES": true, + "HOSTALIASES": true, + "LD_AUDIT": true, + "LD_DEBUG": true, + "LD_DEBUG_OUTPUT": true, + "LD_DYNAMIC_WEAK": true, + "LD_HWCAP_MASK": true, + "LD_LIBRARY_PATH": true, + "LD_ORIGIN_PATH": true, + "LD_PRELOAD": true, + "LD_PROFILE": true, + "LD_SHOW_AUXV": true, + "LD_USE_LOAD_BIAS": true, + "LOCALDOMAIN": true, + "LOCPATH": true, + "MALLOC_TRACE": true, + "NIS_PATH": true, + "NLSPATH": true, + "RESOLV_HOST_CONF": true, + "RES_OPTIONS": true, + "TMPDIR": true, + "TZDIR": true, +} + +const PreservedUnsafePrefix = "SNAP_SAVED_" + +// envMap creates a map from the given environment string list, +// e.g. the list returned from os.Environ(). If preserveUnsafeVars +// rename variables that will be stripped out by the dynamic linker +// executing the setuid snap-confine by prepending their names with +// PreservedUnsafePrefix. +func envMap(env []string, preserveUnsafeEnv preserveUnsafeEnvFlag) map[string]string { envMap := map[string]string{} for _, kv := range env { + // snap-exec unconditionally renames variables + // starting with PreservedUnsafePrefix so skip any + // that are already present in the environment to + // avoid confusion. + if strings.HasPrefix(kv, PreservedUnsafePrefix) { + continue + } l := strings.SplitN(kv, "=", 2) if len(l) < 2 { continue // strange } k, v := l[0], l[1] + if preserveUnsafeEnv == preserveUnsafeFlag && unsafeEnv[k] { + k = PreservedUnsafePrefix + k + } envMap[k] = v } return envMap diff -Nru snapd-2.28.5/snap/snapenv/snapenv_test.go snapd-2.29.3/snap/snapenv/snapenv_test.go --- snapd-2.28.5/snap/snapenv/snapenv_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/snap/snapenv/snapenv_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -23,6 +23,7 @@ "fmt" "os" "os/user" + "strings" "testing" . "gopkg.in/check.v1" @@ -30,11 +31,14 @@ "github.com/snapcore/snapd/arch" "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/snap" + "github.com/snapcore/snapd/testutil" ) func Test(t *testing.T) { TestingT(t) } -type HTestSuite struct{} +type HTestSuite struct { + testutil.BaseTest +} var _ = Suite(&HTestSuite{}) @@ -137,18 +141,58 @@ } } +func envValue(env []string, key string) (bool, string) { + for _, item := range env { + if strings.HasPrefix(item, key+"=") { + return true, strings.SplitN(item, "=", 2)[1] + } + } + return false, "" +} + func (s *HTestSuite) TestExtraEnvForExecEnv(c *C) { info, err := snap.InfoFromSnapYaml(mockYaml) c.Assert(err, IsNil) info.SideInfo.Revision = snap.R(42) env := ExecEnv(info, map[string]string{"FOO": "BAR"}) - found := false - for _, item := range env { - if item == "FOO=BAR" { - found = true - break - } + found, val := envValue(env, "FOO") + c.Assert(found, Equals, true) + c.Assert(val, Equals, "BAR") +} + +func setenvWithReset(s *HTestSuite, key string, val string) { + tmpdirEnv, tmpdirFound := os.LookupEnv("TMPDIR") + os.Setenv("TMPDIR", "/var/tmp") + if tmpdirFound { + s.AddCleanup(func() { os.Setenv("TMPDIR", tmpdirEnv) }) + } else { + s.AddCleanup(func() { os.Unsetenv("TMPDIR") }) } +} + +func (s *HTestSuite) TestExecEnvNoRenameTMPDIRForNonClassic(c *C) { + setenvWithReset(s, "TMPDIR", "/var/tmp") + + env := ExecEnv(mockSnapInfo, map[string]string{}) + + found, val := envValue(env, "TMPDIR") + c.Assert(found, Equals, true) + c.Assert(val, Equals, "/var/tmp") + + found, _ = envValue(env, PreservedUnsafePrefix+"TMPDIR") + c.Assert(found, Equals, false) +} + +func (s *HTestSuite) TestExecEnvRenameTMPDIRForClassic(c *C) { + setenvWithReset(s, "TMPDIR", "/var/tmp") + + env := ExecEnv(mockClassicSnapInfo, map[string]string{}) + + found, _ := envValue(env, "TMPDIR") + c.Assert(found, Equals, false) + + found, val := envValue(env, PreservedUnsafePrefix+"TMPDIR") c.Assert(found, Equals, true) + c.Assert(val, Equals, "/var/tmp") } diff -Nru snapd-2.28.5/snap/snaptest/build.go snapd-2.29.3/snap/snaptest/build.go --- snapd-2.28.5/snap/snaptest/build.go 2016-11-24 09:36:04.000000000 +0000 +++ snapd-2.29.3/snap/snaptest/build.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,270 +0,0 @@ -// -*- 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 . - * - */ - -package snaptest - -// TODO: replace this using some subset from snapcraft or simplify further! - -import ( - "bufio" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "regexp" - "strings" - "syscall" - - "github.com/snapcore/snapd/osutil" - "github.com/snapcore/snapd/snap" - "github.com/snapcore/snapd/snap/squashfs" -) - -// from click's click.build.ClickBuilderBase, and there from -// @Dpkg::Source::Package::tar_ignore_default_pattern; -// changed to regexps from globs for sanity (hah) -// -// Please resist the temptation of optimizing the regexp by grouping -// things by hand. People will find it unreadable enough as it is. -var shouldExcludeDefault = regexp.MustCompile(strings.Join([]string{ - `\.snap$`, // added - `\.click$`, - `^\..*\.sw.$`, - `~$`, - `^,,`, - `^\.[#~]`, - `^\.arch-ids$`, - `^\.arch-inventory$`, - `^\.bzr$`, - `^\.bzr-builddeb$`, - `^\.bzr\.backup$`, - `^\.bzr\.tags$`, - `^\.bzrignore$`, - `^\.cvsignore$`, - `^\.git$`, - `^\.gitattributes$`, - `^\.gitignore$`, - `^\.gitmodules$`, - `^\.hg$`, - `^\.hgignore$`, - `^\.hgsigs$`, - `^\.hgtags$`, - `^\.shelf$`, - `^\.svn$`, - `^CVS$`, - `^DEADJOE$`, - `^RCS$`, - `^_MTN$`, - `^_darcs$`, - `^{arch}$`, - `^\.snapignore$`, -}, "|")).MatchString - -// fake static function variables -type keep struct { - basedir string - exclude func(string) bool -} - -func (k *keep) shouldExclude(basedir string, file string) bool { - if basedir == k.basedir { - if k.exclude == nil { - return false - } - - return k.exclude(file) - } - - k.basedir = basedir - k.exclude = nil - - snapignore, err := os.Open(filepath.Join(basedir, ".snapignore")) - if err != nil { - return false - } - - scanner := bufio.NewScanner(snapignore) - var lines []string - for scanner.Scan() { - line := scanner.Text() - if _, err := regexp.Compile(line); err != nil { - // not a regexp - line = regexp.QuoteMeta(line) - } - lines = append(lines, line) - } - - fullRegex := strings.Join(lines, "|") - exclude, err := regexp.Compile(fullRegex) - if err == nil { - k.exclude = exclude.MatchString - - return k.exclude(file) - } - - // can't happen; can't even find a way to trigger it in testing. - panic(fmt.Sprintf("|-composition of valid regexps is invalid?!? Please report this bug: %#v", fullRegex)) -} - -var shouldExcludeDynamic = new(keep).shouldExclude - -func shouldExclude(basedir string, file string) bool { - return shouldExcludeDefault(file) || shouldExcludeDynamic(basedir, file) -} - -// small helper that return the architecture or "multi" if its multiple arches -func debArchitecture(info *snap.Info) string { - switch len(info.Architectures) { - case 0: - return "unknown" - case 1: - return info.Architectures[0] - default: - return "multi" - } -} - -func copyToBuildDir(sourceDir, buildDir string) error { - sourceDir, err := filepath.Abs(sourceDir) - if err != nil { - return err - } - - err = os.Remove(buildDir) - if err != nil && !os.IsNotExist(err) { - // this shouldn't happen, but. - return err - } - - // no umask here so that we get the permissions correct - oldUmask := syscall.Umask(0) - defer syscall.Umask(oldUmask) - - return filepath.Walk(sourceDir, func(path string, info os.FileInfo, errin error) (err error) { - if errin != nil { - return errin - } - - relpath := path[len(sourceDir):] - if relpath == "/DEBIAN" || shouldExclude(sourceDir, filepath.Base(path)) { - if info.IsDir() { - return filepath.SkipDir - } - return nil - } - - dest := filepath.Join(buildDir, relpath) - - // handle dirs - if info.IsDir() { - if err := os.Mkdir(dest, info.Mode()); err != nil { - return err - } - // ensure that permissions are preserved - uid := int(info.Sys().(*syscall.Stat_t).Uid) - gid := int(info.Sys().(*syscall.Stat_t).Gid) - return os.Chown(dest, uid, gid) - } - - // handle char/block devices - if osutil.IsDevice(info.Mode()) { - return osutil.CopySpecialFile(path, dest) - } - - if (info.Mode() & os.ModeSymlink) != 0 { - target, err := os.Readlink(path) - if err != nil { - return err - } - return os.Symlink(target, dest) - } - - // fail if its unsupported - if !info.Mode().IsRegular() { - return fmt.Errorf("cannot handle type of file %s", path) - } - - // it's a file. Maybe we can link it? - if os.Link(path, dest) == nil { - // whee - return nil - } - // sigh. ok, copy it is. - return osutil.CopyFile(path, dest, osutil.CopyFlagDefault) - }) -} - -func prepare(sourceDir, targetDir, buildDir string) (snapName string, err error) { - // ensure we have valid content - yaml, err := ioutil.ReadFile(filepath.Join(sourceDir, "meta", "snap.yaml")) - if err != nil { - return "", err - } - - info, err := snap.InfoFromSnapYaml(yaml) - if err != nil { - return "", err - } - - err = snap.Validate(info) - if err != nil { - return "", err - } - - if err := copyToBuildDir(sourceDir, buildDir); err != nil { - return "", err - } - - // build the package - snapName = fmt.Sprintf("%s_%s_%v.snap", info.Name(), info.Version, debArchitecture(info)) - - if targetDir != "" { - snapName = filepath.Join(targetDir, snapName) - if _, err := os.Stat(targetDir); os.IsNotExist(err) { - if err := os.MkdirAll(targetDir, 0755); err != nil { - return "", err - } - } - } - - return snapName, nil -} - -// BuildSquashfsSnap the given sourceDirectory and return the generated -// snap file -func BuildSquashfsSnap(sourceDir, targetDir string) (string, error) { - // create build dir - buildDir, err := ioutil.TempDir("", "snappy-build-") - if err != nil { - return "", err - } - defer os.RemoveAll(buildDir) - - snapName, err := prepare(sourceDir, targetDir, buildDir) - if err != nil { - return "", err - } - - d := squashfs.New(snapName) - if err = d.Build(buildDir); err != nil { - return "", err - } - - return snapName, nil -} diff -Nru snapd-2.28.5/snap/snaptest/build_test.go snapd-2.29.3/snap/snaptest/build_test.go --- snapd-2.28.5/snap/snaptest/build_test.go 2016-11-24 09:36:04.000000000 +0000 +++ snapd-2.29.3/snap/snaptest/build_test.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,283 +0,0 @@ -// -*- 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 . - * - */ - -package snaptest_test - -import ( - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "regexp" - "strings" - "syscall" - - "github.com/snapcore/snapd/dirs" - "github.com/snapcore/snapd/snap" - "github.com/snapcore/snapd/snap/snaptest" - "github.com/snapcore/snapd/testutil" - - . "gopkg.in/check.v1" -) - -type BuildTestSuite struct { - testutil.BaseTest -} - -var _ = Suite(&BuildTestSuite{}) - -func (s *BuildTestSuite) SetUpTest(c *C) { - s.BaseTest.SetUpTest(c) - - // chdir into a tempdir - pwd, err := os.Getwd() - c.Assert(err, IsNil) - s.AddCleanup(func() { os.Chdir(pwd) }) - err = os.Chdir(c.MkDir()) - c.Assert(err, IsNil) - - // use fake root - dirs.SetRootDir(c.MkDir()) -} - -func makeExampleSnapSourceDir(c *C, snapYamlContent string) string { - tempdir := c.MkDir() - - // use meta/snap.yaml - metaDir := filepath.Join(tempdir, "meta") - err := os.Mkdir(metaDir, 0755) - c.Assert(err, IsNil) - err = ioutil.WriteFile(filepath.Join(metaDir, "snap.yaml"), []byte(snapYamlContent), 0644) - c.Assert(err, IsNil) - - const helloBinContent = `#!/bin/sh -printf "hello world" -` - - // an example binary - binDir := filepath.Join(tempdir, "bin") - err = os.Mkdir(binDir, 0755) - c.Assert(err, IsNil) - err = ioutil.WriteFile(filepath.Join(binDir, "hello-world"), []byte(helloBinContent), 0755) - c.Assert(err, IsNil) - - // unusual permissions for dir - tmpDir := filepath.Join(tempdir, "tmp") - err = os.Mkdir(tmpDir, 0755) - c.Assert(err, IsNil) - // avoid umask - err = os.Chmod(tmpDir, 01777) - c.Assert(err, IsNil) - - // and file - someFile := filepath.Join(tempdir, "file-with-perm") - err = ioutil.WriteFile(someFile, []byte(""), 0666) - c.Assert(err, IsNil) - err = os.Chmod(someFile, 0666) - c.Assert(err, IsNil) - - // an example symlink - err = os.Symlink("bin/hello-world", filepath.Join(tempdir, "symlink")) - c.Assert(err, IsNil) - - return tempdir -} - -func (s *BuildTestSuite) TestBuildNoManifestFails(c *C) { - sourceDir := makeExampleSnapSourceDir(c, "") - c.Assert(os.Remove(filepath.Join(sourceDir, "meta", "snap.yaml")), IsNil) - _, err := snaptest.BuildSquashfsSnap(sourceDir, "") - c.Assert(err, NotNil) // XXX maybe make the error more explicit -} - -func (s *BuildTestSuite) TestCopyCopies(c *C) { - sourceDir := makeExampleSnapSourceDir(c, "name: hello") - // actually this'll be on /tmp so it'll be a link - target := c.MkDir() - c.Assert(snaptest.CopyToBuildDir(sourceDir, target), IsNil) - out, err := exec.Command("diff", "-qrN", sourceDir, target).Output() - c.Check(err, IsNil) - c.Check(out, DeepEquals, []byte{}) -} - -func (s *BuildTestSuite) TestCopyActuallyCopies(c *C) { - sourceDir := makeExampleSnapSourceDir(c, "name: hello") - - // hoping to get the non-linking behaviour via /dev/shm - target, err := ioutil.TempDir("/dev/shm", "copy") - // sbuild environments won't allow writing to /dev/shm, so its - // ok to skip there - if os.IsPermission(err) { - c.Skip("/dev/shm is not writable for us") - } - c.Assert(err, IsNil) - - c.Assert(snaptest.CopyToBuildDir(sourceDir, target), IsNil) - out, err := exec.Command("diff", "-qrN", sourceDir, target).Output() - c.Check(err, IsNil) - c.Check(out, DeepEquals, []byte{}) -} - -func (s *BuildTestSuite) TestCopyExcludesBackups(c *C) { - sourceDir := makeExampleSnapSourceDir(c, "name: hello") - target := c.MkDir() - // add a backup file - c.Assert(ioutil.WriteFile(filepath.Join(sourceDir, "foo~"), []byte("hi"), 0755), IsNil) - c.Assert(snaptest.CopyToBuildDir(sourceDir, target), IsNil) - cmd := exec.Command("diff", "-qr", sourceDir, target) - cmd.Env = append(cmd.Env, "LANG=C") - out, err := cmd.Output() - c.Check(err, NotNil) - c.Check(string(out), Matches, `(?m)Only in \S+: foo~`) -} - -func (s *BuildTestSuite) TestCopyExcludesTopLevelDEBIAN(c *C) { - sourceDir := makeExampleSnapSourceDir(c, "name: hello") - target := c.MkDir() - // add a toplevel DEBIAN - c.Assert(os.MkdirAll(filepath.Join(sourceDir, "DEBIAN", "foo"), 0755), IsNil) - // and a non-toplevel DEBIAN - c.Assert(os.MkdirAll(filepath.Join(sourceDir, "bar", "DEBIAN", "baz"), 0755), IsNil) - c.Assert(snaptest.CopyToBuildDir(sourceDir, target), IsNil) - cmd := exec.Command("diff", "-qr", sourceDir, target) - cmd.Env = append(cmd.Env, "LANG=C") - out, err := cmd.Output() - c.Check(err, NotNil) - c.Check(string(out), Matches, `(?m)Only in \S+: DEBIAN`) - // but *only one* DEBIAN is skipped - c.Check(strings.Count(string(out), "Only in"), Equals, 1) -} - -func (s *BuildTestSuite) TestCopyExcludesWholeDirs(c *C) { - sourceDir := makeExampleSnapSourceDir(c, "name: hello") - target := c.MkDir() - // add a file inside a skipped dir - c.Assert(os.Mkdir(filepath.Join(sourceDir, ".bzr"), 0755), IsNil) - c.Assert(ioutil.WriteFile(filepath.Join(sourceDir, ".bzr", "foo"), []byte("hi"), 0755), IsNil) - c.Assert(snaptest.CopyToBuildDir(sourceDir, target), IsNil) - out, _ := exec.Command("find", sourceDir).Output() - c.Check(string(out), Not(Equals), "") - cmd := exec.Command("diff", "-qr", sourceDir, target) - cmd.Env = append(cmd.Env, "LANG=C") - out, err := cmd.Output() - c.Check(err, NotNil) - c.Check(string(out), Matches, `(?m)Only in \S+: \.bzr`) -} - -func (s *BuildTestSuite) TestExcludeDynamicFalseIfNoSnapignore(c *C) { - basedir := c.MkDir() - c.Check(snaptest.ShouldExcludeDynamic(basedir, "foo"), Equals, false) -} - -func (s *BuildTestSuite) TestExcludeDynamicWorksIfSnapignore(c *C) { - basedir := c.MkDir() - c.Assert(ioutil.WriteFile(filepath.Join(basedir, ".snapignore"), []byte("foo\nb.r\n"), 0644), IsNil) - c.Check(snaptest.ShouldExcludeDynamic(basedir, "foo"), Equals, true) - c.Check(snaptest.ShouldExcludeDynamic(basedir, "bar"), Equals, true) - c.Check(snaptest.ShouldExcludeDynamic(basedir, "bzr"), Equals, true) - c.Check(snaptest.ShouldExcludeDynamic(basedir, "baz"), Equals, false) -} - -func (s *BuildTestSuite) TestExcludeDynamicWeirdRegexps(c *C) { - basedir := c.MkDir() - c.Assert(ioutil.WriteFile(filepath.Join(basedir, ".snapignore"), []byte("*hello\n"), 0644), IsNil) - // note "*hello" is not a valid regexp, so will be taken literally (not globbed!) - c.Check(snaptest.ShouldExcludeDynamic(basedir, "ahello"), Equals, false) - c.Check(snaptest.ShouldExcludeDynamic(basedir, "*hello"), Equals, true) -} - -func (s *BuildTestSuite) TestDebArchitecture(c *C) { - c.Check(snaptest.DebArchitecture(&snap.Info{Architectures: []string{"foo"}}), Equals, "foo") - c.Check(snaptest.DebArchitecture(&snap.Info{Architectures: []string{"foo", "bar"}}), Equals, "multi") - c.Check(snaptest.DebArchitecture(&snap.Info{Architectures: nil}), Equals, "unknown") -} - -func (s *BuildTestSuite) TestBuildFailsForUnknownType(c *C) { - sourceDir := makeExampleSnapSourceDir(c, `name: hello -version: 1.0.1 -`) - err := syscall.Mkfifo(filepath.Join(sourceDir, "fifo"), 0644) - c.Assert(err, IsNil) - - _, err = snaptest.BuildSquashfsSnap(sourceDir, "") - c.Assert(err, ErrorMatches, "cannot handle type of file .*") -} - -func (s *BuildTestSuite) TestBuildSquashfsSimple(c *C) { - sourceDir := makeExampleSnapSourceDir(c, `name: hello -version: 1.0.1 -architectures: ["i386", "amd64"] -integration: - app: - apparmor-profile: meta/hello.apparmor -`) - - resultSnap, err := snaptest.BuildSquashfsSnap(sourceDir, "") - c.Assert(err, IsNil) - - // check that there is result - _, err = os.Stat(resultSnap) - c.Assert(err, IsNil) - c.Assert(resultSnap, Equals, "hello_1.0.1_multi.snap") - - // check that the content looks sane - output, err := exec.Command("unsquashfs", "-ll", "hello_1.0.1_multi.snap").CombinedOutput() - c.Assert(err, IsNil) - for _, needle := range []string{ - "meta/snap.yaml", - "bin/hello-world", - "symlink -> bin/hello-world", - } { - expr := fmt.Sprintf(`(?ms).*%s.*`, regexp.QuoteMeta(needle)) - c.Assert(string(output), Matches, expr) - } -} - -func (s *BuildTestSuite) TestBuildSimpleOutputDir(c *C) { - sourceDir := makeExampleSnapSourceDir(c, `name: hello -version: 1.0.1 -architectures: ["i386", "amd64"] -integration: - app: - apparmor-profile: meta/hello.apparmor -`) - - outputDir := filepath.Join(c.MkDir(), "output") - snapOutput := filepath.Join(outputDir, "hello_1.0.1_multi.snap") - resultSnap, err := snaptest.BuildSquashfsSnap(sourceDir, outputDir) - c.Assert(err, IsNil) - - // check that there is result - _, err = os.Stat(resultSnap) - c.Assert(err, IsNil) - c.Assert(resultSnap, Equals, snapOutput) - - // check that the content looks sane - output, err := exec.Command("unsquashfs", "-ll", resultSnap).CombinedOutput() - c.Assert(err, IsNil) - for _, needle := range []string{ - "meta/snap.yaml", - "bin/hello-world", - "symlink -> bin/hello-world", - } { - expr := fmt.Sprintf(`(?ms).*%s.*`, regexp.QuoteMeta(needle)) - c.Assert(string(output), Matches, expr) - } -} diff -Nru snapd-2.28.5/snap/snaptest/export_test.go snapd-2.29.3/snap/snaptest/export_test.go --- snapd-2.28.5/snap/snaptest/export_test.go 2016-05-03 05:50:48.000000000 +0000 +++ snapd-2.29.3/snap/snaptest/export_test.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -// -*- 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 . - * - */ - -package snaptest - -var ( - CopyToBuildDir = copyToBuildDir - ShouldExcludeDynamic = shouldExcludeDynamic - DebArchitecture = debArchitecture -) diff -Nru snapd-2.28.5/snap/snaptest/snaptest.go snapd-2.29.3/snap/snaptest/snaptest.go --- snapd-2.28.5/snap/snaptest/snaptest.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/snap/snaptest/snaptest.go 2017-10-23 06:17:27.000000000 +0000 @@ -29,6 +29,7 @@ "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/snap" + "github.com/snapcore/snapd/snap/pack" ) // MockSnap puts a snap.yaml file on disk so to mock an installed snap, based on the provided arguments. @@ -134,7 +135,7 @@ err = osutil.ChDir(snapSource, func() error { var err error - snapFilePath, err = BuildSquashfsSnap(snapSource, "") + snapFilePath, err = pack.Snap(snapSource, "") return err }) if err != nil { diff -Nru snapd-2.28.5/snap/validate.go snapd-2.29.3/snap/validate.go --- snapd-2.28.5/snap/validate.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/snap/validate.go 2017-10-23 06:17:27.000000000 +0000 @@ -30,11 +30,12 @@ // Regular expression describing correct identifiers. var validSnapName = regexp.MustCompile("^(?:[a-z0-9]+-?)*[a-z](?:-?[a-z0-9])*$") -var validEpoch = regexp.MustCompile("^(?:0|[1-9][0-9]*[*]?)$") var validHookName = regexp.MustCompile("^[a-z](?:-?[a-z0-9])*$") // ValidateName checks if a string can be used as a snap name. func ValidateName(name string) error { + // NOTE: This function should be synchronized with the two other + // implementations: sc_snap_name_validate and validate_snap_name . valid := validSnapName.MatchString(name) if !valid { return fmt.Errorf("invalid snap name: %q", name) @@ -42,15 +43,6 @@ return nil } -// ValidateEpoch checks if a string can be used as a snap epoch. -func ValidateEpoch(epoch string) error { - valid := validEpoch.MatchString(epoch) - if !valid { - return fmt.Errorf("invalid snap epoch: %q", epoch) - } - return nil -} - // ValidateLicense checks if a string is a valid SPDX expression. func ValidateLicense(license string) error { if err := spdx.ValidateLicense(license); err != nil { @@ -90,11 +82,7 @@ return err } - epoch := info.Epoch - if epoch == "" { - return fmt.Errorf("snap epoch cannot be empty") - } - err = ValidateEpoch(epoch) + err = info.Epoch.Validate() if err != nil { return err } @@ -162,8 +150,9 @@ } // appContentWhitelist is the whitelist of legal chars in the "apps" -// section of snap.yaml -var appContentWhitelist = regexp.MustCompile(`^[A-Za-z0-9/. _#:-]*$`) +// section of snap.yaml. Do not allow any of [',",`] here or snap-exec +// will get confused. +var appContentWhitelist = regexp.MustCompile(`^[A-Za-z0-9/. _#:$-]*$`) var validAppName = regexp.MustCompile("^[a-zA-Z0-9](?:-?[a-zA-Z0-9])*$") // ValidateApp verifies the content in the app info. diff -Nru snapd-2.28.5/snap/validate_test.go snapd-2.29.3/snap/validate_test.go --- snapd-2.28.5/snap/validate_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/snap/validate_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -65,23 +65,6 @@ } } -func (s *ValidateSuite) TestValidateEpoch(c *C) { - validEpochs := []string{ - "0", "1*", "1", "400*", "1234", - } - for _, epoch := range validEpochs { - err := ValidateEpoch(epoch) - c.Assert(err, IsNil) - } - invalidEpochs := []string{ - "0*", "_", "1-", "1+", "-1", "+1", "-1*", "a", "1a", "1**", - } - for _, epoch := range invalidEpochs { - err := ValidateEpoch(epoch) - c.Assert(err, ErrorMatches, `invalid snap epoch: ".*"`) - } -} - func (s *ValidateSuite) TestValidateLicense(c *C) { validLicenses := []string{ "GPL-3.0", "(GPL-3.0)", "GPL-3.0+", "GPL-3.0 AND GPL-2.0", "GPL-3.0 OR GPL-2.0", "MIT OR (GPL-3.0 AND GPL-2.0)", "MIT OR(GPL-3.0 AND GPL-2.0)", @@ -155,6 +138,12 @@ c.Check(ValidateApp(&AppInfo{Name: "foo", PostStopCommand: "foo"}), IsNil) } +func (s *ValidateSuite) TestAppWhitelistWithVars(c *C) { + c.Check(ValidateApp(&AppInfo{Name: "foo", Command: "foo $SNAP_DATA"}), IsNil) + c.Check(ValidateApp(&AppInfo{Name: "foo", StopCommand: "foo $SNAP_DATA"}), IsNil) + c.Check(ValidateApp(&AppInfo{Name: "foo", PostStopCommand: "foo $SNAP_DATA"}), IsNil) +} + func (s *ValidateSuite) TestAppWhitelistIllegal(c *C) { c.Check(ValidateApp(&AppInfo{Name: "x\n"}), NotNil) c.Check(ValidateApp(&AppInfo{Name: "test!me"}), NotNil) @@ -190,7 +179,7 @@ func (s *ValidateSuite) TestAppWhitelistError(c *C) { err := ValidateApp(&AppInfo{Name: "foo", Command: "x\n"}) c.Assert(err, NotNil) - c.Check(err.Error(), Equals, `app description field 'command' contains illegal "x\n" (legal: '^[A-Za-z0-9/. _#:-]*$')`) + c.Check(err.Error(), Equals, `app description field 'command' contains illegal "x\n" (legal: '^[A-Za-z0-9/. _#:$-]*$')`) } // Validate @@ -243,14 +232,11 @@ } func (s *ValidateSuite) TestIllegalSnapEpoch(c *C) { - info, err := InfoFromSnapYaml([]byte(`name: foo + _, err := InfoFromSnapYaml([]byte(`name: foo version: 1.0 epoch: 0* `)) - c.Assert(err, IsNil) - - err = Validate(info) - c.Check(err, ErrorMatches, `invalid snap epoch: "0\*"`) + c.Assert(err, ErrorMatches, `.*invalid epoch.*`) } func (s *ValidateSuite) TestMissingSnapEpochIsOkay(c *C) { diff -Nru snapd-2.28.5/spread.yaml snapd-2.29.3/spread.yaml --- snapd-2.28.5/spread.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/spread.yaml 2017-10-30 08:20:54.000000000 +0000 @@ -18,6 +18,7 @@ MODIFY_CORE_SNAP_FOR_REEXEC: "$(HOST: echo ${SPREAD_MODIFY_CORE_SNAP_FOR_REEXEC:-1})" SPREAD_STORE_USER: "$(HOST: echo $SPREAD_STORE_USER)" SPREAD_STORE_PASSWORD: "$(HOST: echo $SPREAD_STORE_PASSWORD)" + SPREAD_DEBUG_EACH: "$(HOST: echo ${SPREAD_DEBUG_EACH:-1})" LANG: "$(echo ${LANG:-C.UTF-8})" LANGUAGE: "$(echo ${LANGUAGE:-en})" # important to ensure adhoc and linode/qemu behave the same @@ -39,8 +40,6 @@ NEW_CORE_CHANNEL: "$(HOST: echo $SPREAD_NEW_CORE_CHANNEL)" SRU_VALIDATION: "$(HOST: echo ${SPREAD_SRU_VALIDATION:-0})" PRE_CACHE_SNAPS: core ubuntu-core test-snapd-tools - # This skips various sync calls which can greatly speed up test execution. - SNAPD_UNSAFE_IO: 1 backends: linode: @@ -102,6 +101,9 @@ - ubuntu-17.10-64: username: ubuntu password: ubuntu + - ubuntu-18.04-64: + username: ubuntu + password: ubuntu - debian-9-64: username: debian password: debian @@ -180,6 +182,19 @@ - ubuntu-17.10-armhf: username: ubuntu password: ubuntu + # Bionic + - ubuntu-18.04-amd64: + username: ubuntu + password: ubuntu + - ubuntu-18.04-i386: + username: ubuntu + password: ubuntu + - ubuntu-18.04-ppc64el: + username: ubuntu + password: ubuntu + - ubuntu-18.04-armhf: + username: ubuntu + password: ubuntu external: type: adhoc environment: @@ -260,14 +275,16 @@ dmesg -c > /dev/null debug-each: | - echo '# journal messages for snapd' - journalctl -u snapd - echo '# apparmor denials ' - dmesg --ctime | grep DENIED || true - echo '# seccomp denials (kills) ' - dmesg --ctime | grep type=1326 || true - echo '# snap interfaces' - snap interfaces || true + if [ "$SPREAD_DEBUG_EACH" = 1 ]; then + echo '# journal messages for snapd' + journalctl -u snapd + echo '# apparmor denials ' + dmesg --ctime | grep DENIED || true + echo '# seccomp denials (kills) ' + dmesg --ctime | grep type=1326 || true + echo '# snap interfaces' + snap interfaces || true + fi rename: # Move content into a directory, so that deltas computed by repack benefit @@ -299,6 +316,21 @@ echo "Tests cannot run inside a container" exit 1 fi + if [[ "$SPREAD_SYSTEM" == debian-unstable-* ]]; then + # There's a packaging bug in Debian sid lately where some packages + # conflict on manual page file. To work around it simply remove the + # manpages package. + apt-get remove --purge -y manpages + fi + + # FIXME: remove once the following bug is fixed: + # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=876128 + if [[ "$SPREAD_SYSTEM" == debian-unstable-* ]]; then + # There's a packaging bug in Debian sid lately where some packages + # conflict on manual page file. To work around it simply remove the + # manpages package. + apt-get remove --purge -y manpages + fi # apt update is hanging on security.ubuntu.com with IPv6, prefer IPv4 over IPv6 cat < gai.conf diff -Nru snapd-2.28.5/store/auth.go snapd-2.29.3/store/auth.go --- snapd-2.28.5/store/auth.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/store/auth.go 2017-10-27 06:13:10.000000000 +0000 @@ -33,12 +33,7 @@ ) var ( - baseAPIURL = apiURL() - // DeviceNonceAPI points to endpoint to get a nonce - DeviceNonceAPI = baseAPIURL.String() + "api/v1/snaps/auth/nonces" - // DeviceSessionAPI points to endpoint to get a device session - DeviceSessionAPI = baseAPIURL.String() + "api/v1/snaps/auth/sessions" - myappsAPIBase = myappsURL() + myappsAPIBase = myappsURL() // MyAppsMacaroonACLAPI points to MyApps endpoint to get a ACL macaroon MyAppsMacaroonACLAPI = myappsAPIBase + "dev/api/acl/" ubuntuoneAPIBase = authURL() @@ -197,6 +192,8 @@ return "", ErrAuthenticationNeeds2fa case "TWOFACTOR_FAILURE": return "", Err2faFailed + case "INVALID_CREDENTIALS": + return "", ErrInvalidCredentials case "INVALID_DATA": return "", InvalidAuthDataError(msg.Extra) case "PASSWORD_POLICY_ERROR": @@ -242,7 +239,7 @@ } // requestStoreDeviceNonce requests a nonce for device authentication against the store. -func requestStoreDeviceNonce() (string, error) { +func requestStoreDeviceNonce(deviceNonceEndpoint string) (string, error) { const errorPrefix = "cannot get nonce from store: " var responseData struct { @@ -253,7 +250,7 @@ "User-Agent": httputil.UserAgent(), "Accept": "application/json", } - resp, err := retryPostRequestDecodeJSON(DeviceNonceAPI, headers, nil, &responseData, nil) + resp, err := retryPostRequestDecodeJSON(deviceNonceEndpoint, headers, nil, &responseData, nil) if err != nil { return "", fmt.Errorf(errorPrefix+"%v", err) } @@ -276,7 +273,7 @@ } // requestDeviceSession requests a device session macaroon from the store. -func requestDeviceSession(paramsEncoder deviceSessionRequestParamsEncoder, previousSession string) (string, error) { +func requestDeviceSession(deviceSessionEndpoint string, paramsEncoder deviceSessionRequestParamsEncoder, previousSession string) (string, error) { const errorPrefix = "cannot get device session from store: " data := map[string]string{ @@ -303,7 +300,7 @@ headers["X-Device-Authorization"] = fmt.Sprintf(`Macaroon root="%s"`, previousSession) } - _, err = retryPostRequest(DeviceSessionAPI, headers, deviceJSONData, func(resp *http.Response) error { + _, err = retryPostRequest(deviceSessionEndpoint, headers, deviceJSONData, func(resp *http.Response) error { if resp.StatusCode == 200 || resp.StatusCode == 202 { return json.NewDecoder(resp.Body).Decode(&responseData) } diff -Nru snapd-2.28.5/store/auth_test.go snapd-2.29.3/store/auth_test.go --- snapd-2.28.5/store/auth_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/store/auth_test.go 2017-10-27 06:13:10.000000000 +0000 @@ -172,7 +172,7 @@ UbuntuoneDischargeAPI = mockServer.URL + "/tokens/discharge" discharge, err := dischargeAuthCaveat("third-party-caveat", "foo@example.com", "passwd", "") - c.Assert(err, ErrorMatches, "cannot authenticate to snap store: Provided email/password is not correct.") + c.Assert(err, Equals, ErrInvalidCredentials) c.Assert(discharge, Equals, "") } @@ -221,7 +221,7 @@ UbuntuoneRefreshDischargeAPI = mockServer.URL + "/tokens/refresh" discharge, err := refreshDischargeMacaroon("soft-expired-serialized-discharge-macaroon") - c.Assert(err, ErrorMatches, "cannot authenticate to snap store: Provided email/password is not correct.") + c.Assert(err, Equals, ErrInvalidCredentials) c.Assert(discharge, Equals, "") } @@ -283,9 +283,9 @@ io.WriteString(w, mockStoreReturnNonce) })) defer mockServer.Close() - DeviceNonceAPI = mockServer.URL + "/api/v1/snaps/auth/nonces" - nonce, err := requestStoreDeviceNonce() + deviceNonceAPI := mockServer.URL + "/api/v1/snaps/auth/nonces" + nonce, err := requestStoreDeviceNonce(deviceNonceAPI) c.Assert(err, IsNil) c.Assert(nonce, Equals, "the-nonce") } @@ -301,9 +301,9 @@ } })) defer mockServer.Close() - DeviceNonceAPI = mockServer.URL + "/api/v1/snaps/auth/nonces" - nonce, err := requestStoreDeviceNonce() + deviceNonceAPI := mockServer.URL + "/api/v1/snaps/auth/nonces" + nonce, err := requestStoreDeviceNonce(deviceNonceAPI) c.Assert(err, IsNil) c.Assert(nonce, Equals, "the-nonce") c.Assert(n, Equals, 4) @@ -316,17 +316,17 @@ w.WriteHeader(500) })) defer mockServer.Close() - DeviceNonceAPI = mockServer.URL + "/api/v1/snaps/auth/nonces" - _, err := requestStoreDeviceNonce() + deviceNonceAPI := mockServer.URL + "/api/v1/snaps/auth/nonces" + _, err := requestStoreDeviceNonce(deviceNonceAPI) c.Assert(err, NotNil) c.Assert(err, ErrorMatches, `cannot get nonce from store: store server returned status 500`) c.Assert(n, Equals, 5) } func (s *authTestSuite) TestRequestStoreDeviceNonceFailureOnDNS(c *C) { - DeviceNonceAPI = "http://nonexistingserver121321.com/api/v1/snaps/auth/nonces" - _, err := requestStoreDeviceNonce() + deviceNonceAPI := "http://nonexistingserver121321.com/api/v1/snaps/auth/nonces" + _, err := requestStoreDeviceNonce(deviceNonceAPI) c.Assert(err, NotNil) c.Assert(err, ErrorMatches, `cannot get nonce from store.*`) } @@ -336,9 +336,9 @@ io.WriteString(w, mockStoreReturnNoNonce) })) defer mockServer.Close() - DeviceNonceAPI = mockServer.URL + "/api/v1/snaps/auth/nonces" - nonce, err := requestStoreDeviceNonce() + deviceNonceAPI := mockServer.URL + "/api/v1/snaps/auth/nonces" + nonce, err := requestStoreDeviceNonce(deviceNonceAPI) c.Assert(err, ErrorMatches, "cannot get nonce from store: empty nonce returned") c.Assert(nonce, Equals, "") } @@ -350,9 +350,9 @@ n++ })) defer mockServer.Close() - DeviceNonceAPI = mockServer.URL + "/api/v1/snaps/auth/nonces" - nonce, err := requestStoreDeviceNonce() + deviceNonceAPI := mockServer.URL + "/api/v1/snaps/auth/nonces" + nonce, err := requestStoreDeviceNonce(deviceNonceAPI) c.Assert(err, ErrorMatches, "cannot get nonce from store: store server returned status 500") c.Assert(n, Equals, 5) c.Assert(nonce, Equals, "") @@ -382,9 +382,9 @@ io.WriteString(w, mockStoreReturnMacaroon) })) defer mockServer.Close() - DeviceSessionAPI = mockServer.URL + "/api/v1/snaps/auth/sessions" - macaroon, err := requestDeviceSession(&testDeviceSessionRequestParamsEncoder{}, "") + deviceSessionAPI := mockServer.URL + "/api/v1/snaps/auth/sessions" + macaroon, err := requestDeviceSession(deviceSessionAPI, &testDeviceSessionRequestParamsEncoder{}, "") c.Assert(err, IsNil) c.Assert(macaroon, Equals, "the-root-macaroon-serialized-data") } @@ -399,9 +399,9 @@ io.WriteString(w, mockStoreReturnMacaroon) })) defer mockServer.Close() - DeviceSessionAPI = mockServer.URL + "/api/v1/snaps/auth/sessions" - macaroon, err := requestDeviceSession(&testDeviceSessionRequestParamsEncoder{}, "previous-session") + deviceSessionAPI := mockServer.URL + "/api/v1/snaps/auth/sessions" + macaroon, err := requestDeviceSession(deviceSessionAPI, &testDeviceSessionRequestParamsEncoder{}, "previous-session") c.Assert(err, IsNil) c.Assert(macaroon, Equals, "the-root-macaroon-serialized-data") } @@ -411,9 +411,9 @@ io.WriteString(w, mockStoreReturnNoMacaroon) })) defer mockServer.Close() - DeviceSessionAPI = mockServer.URL + "/api/v1/snaps/auth/sessions" - macaroon, err := requestDeviceSession(&testDeviceSessionRequestParamsEncoder{}, "") + deviceSessionAPI := mockServer.URL + "/api/v1/snaps/auth/sessions" + macaroon, err := requestDeviceSession(deviceSessionAPI, &testDeviceSessionRequestParamsEncoder{}, "") c.Assert(err, ErrorMatches, "cannot get device session from store: empty session returned") c.Assert(macaroon, Equals, "") } @@ -426,9 +426,9 @@ n++ })) defer mockServer.Close() - DeviceSessionAPI = mockServer.URL + "/api/v1/snaps/auth/sessions" - macaroon, err := requestDeviceSession(&testDeviceSessionRequestParamsEncoder{}, "") + deviceSessionAPI := mockServer.URL + "/api/v1/snaps/auth/sessions" + macaroon, err := requestDeviceSession(deviceSessionAPI, &testDeviceSessionRequestParamsEncoder{}, "") c.Assert(err, ErrorMatches, `cannot get device session from store: store server returned status 500 and body "error body"`) c.Assert(n, Equals, 5) c.Assert(macaroon, Equals, "") diff -Nru snapd-2.28.5/store/cache.go snapd-2.29.3/store/cache.go --- snapd-2.28.5/store/cache.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/store/cache.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,151 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2017 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 . + * + */ + +package store + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "time" + + "github.com/snapcore/snapd/osutil" +) + +// downloadCache is the interface that a store download cache must provide +type downloadCache interface { + // Get gets the given cacheKey content and puts it into targetPath + Get(cacheKey, targetPath string) error + // Put adds a new file to the cache + Put(cacheKey, sourcePath string) error +} + +// nullCache is cache that does not cache +type nullCache struct{} + +func (cm *nullCache) Get(cacheKey, targetPath string) error { + return fmt.Errorf("cannot get items from the nullCache") +} +func (cm *nullCache) Put(cacheKey, sourcePath string) error { return nil } + +// changesByReverseMtime sorts by the mtime of files +type changesByReverseMtime []os.FileInfo + +func (s changesByReverseMtime) Len() int { return len(s) } +func (s changesByReverseMtime) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s changesByReverseMtime) Less(i, j int) bool { return s[i].ModTime().After(s[j].ModTime()) } + +// cacheManager implements a downloadCache via content based hard linking +type CacheManager struct { + cacheDir string + maxItems int +} + +// NewCacheManager returns a new CacheManager with the given cacheDir +// and the given maximum amount of items. The idea behind it is the +// following algorithm: +// +// 1. When starting a download, check if it exists in $cacheDir +// 2. If found, update its mtime, hardlink into target location, and +// return success +// 3. If not found, download the snap +// 4. On success, hardlink into $cacheDir/ +// 5. If cache dir has more than maxItems entries, remove oldest mtimes +// until it has maxItems +// +// The caching part is done here, the downloading happens in the store.go +// code. +func NewCacheManager(cacheDir string, maxItems int) *CacheManager { + return &CacheManager{ + cacheDir: cacheDir, + maxItems: maxItems, + } +} + +// Get gets the given cacheKey content and puts it into targetPath +func (cm *CacheManager) Get(cacheKey, targetPath string) error { + if err := os.Link(cm.path(cacheKey), targetPath); err != nil { + return err + } + now := time.Now() + return os.Chtimes(targetPath, now, now) +} + +// Put adds a new file to the cache with the given cacheKey +func (cm *CacheManager) Put(cacheKey, sourcePath string) error { + // always try to create the cache dir first or the following + // osutil.IsWritable will always fail if the dir is missing + _ = os.MkdirAll(cm.cacheDir, 0700) + + // happens on e.g. `snap download` which runs as the user + if !osutil.IsWritable(cm.cacheDir) { + return nil + } + + err := os.Link(sourcePath, cm.path(cacheKey)) + if os.IsExist(err) { + now := time.Now() + err := os.Chtimes(cm.path(cacheKey), now, now) + // this can happen if a cleanup happens in parallel, ie. + // the file was there but cleanup() removed it between + // the os.Link/os.Chtimes - no biggie, just link it again + if os.IsNotExist(err) { + return os.Link(sourcePath, cm.path(cacheKey)) + } + return err + } + if err != nil { + return err + } + return cm.cleanup() +} + +// Count returns the number of items in the cache +func (cm *CacheManager) Count() int { + if l, err := ioutil.ReadDir(cm.cacheDir); err == nil { + return len(l) + } + return 0 +} + +// path returns the full path of the given content in the cache +func (cm *CacheManager) path(cacheKey string) string { + return filepath.Join(cm.cacheDir, cacheKey) +} + +// cleanup ensures that only maxItems are stored in the cache +func (cm *CacheManager) cleanup() error { + fil, err := ioutil.ReadDir(cm.cacheDir) + if err != nil { + return err + } + if len(fil) <= cm.maxItems { + return nil + } + + sort.Sort(changesByReverseMtime(fil)) + for _, fi := range fil[cm.maxItems:] { + if err := os.Remove(cm.path(fi.Name())); err != nil && os.IsNotExist(err) { + return err + } + } + return nil +} diff -Nru snapd-2.28.5/store/cache_test.go snapd-2.29.3/store/cache_test.go --- snapd-2.28.5/store/cache_test.go 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/store/cache_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,113 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2014-2017 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 . + * + */ + +package store_test + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "strconv" + "time" + + . "gopkg.in/check.v1" + + "github.com/snapcore/snapd/osutil" + "github.com/snapcore/snapd/store" +) + +type cacheSuite struct { + cm *store.CacheManager + tmp string + maxItems int +} + +var _ = Suite(&cacheSuite{}) + +func (s *cacheSuite) SetUpTest(c *C) { + s.tmp = c.MkDir() + + s.maxItems = 5 + s.cm = store.NewCacheManager(c.MkDir(), s.maxItems) + // sanity + c.Check(s.cm.Count(), Equals, 0) +} + +func (s *cacheSuite) makeTestFile(c *C, name, content string) string { + p := filepath.Join(c.MkDir(), name) + err := ioutil.WriteFile(p, []byte(content), 0644) + c.Assert(err, IsNil) + return p +} + +func (s *cacheSuite) TestPutMany(c *C) { + for i := 1; i < s.maxItems+10; i++ { + err := s.cm.Put(fmt.Sprintf("cacheKey-%d", i), s.makeTestFile(c, fmt.Sprintf("f%d", i), fmt.Sprintf("%d", i))) + c.Check(err, IsNil) + if i < s.maxItems { + c.Check(s.cm.Count(), Equals, i) + } else { + c.Check(s.cm.Count(), Equals, s.maxItems) + } + } +} + +func (s *cacheSuite) TestGetNotExistant(c *C) { + err := s.cm.Get("hash-not-in-cache", "some-target-path") + c.Check(err, ErrorMatches, `link .*: no such file or directory`) +} + +func (s *cacheSuite) TestGet(c *C) { + canary := "some content" + p := s.makeTestFile(c, "foo", canary) + err := s.cm.Put("some-cache-key", p) + c.Assert(err, IsNil) + + targetPath := filepath.Join(s.tmp, "new-location") + err = s.cm.Get("some-cache-key", targetPath) + c.Check(err, IsNil) + c.Check(osutil.FileExists(targetPath), Equals, true) + content, err := ioutil.ReadFile(targetPath) + c.Assert(err, IsNil) + c.Check(string(content), Equals, canary) +} + +func (s *cacheSuite) TestClenaup(c *C) { + // add files, add more than + cacheKeys := make([]string, s.maxItems+2) + for i := 0; i < s.maxItems+2; i++ { + p := s.makeTestFile(c, fmt.Sprintf("f%d", i), strconv.Itoa(i)) + cacheKey := fmt.Sprintf("cacheKey-%d", i) + cacheKeys[i] = cacheKey + s.cm.Put(cacheKey, p) + + // mtime is not very granular + time.Sleep(10 * time.Millisecond) + } + c.Check(s.cm.Count(), Equals, s.maxItems) + + // the oldest files are removed from the cache + c.Check(osutil.FileExists(filepath.Join(s.cm.CacheDir(), cacheKeys[0])), Equals, false) + c.Check(osutil.FileExists(filepath.Join(s.cm.CacheDir(), cacheKeys[1])), Equals, false) + + // the newest files are still there + c.Check(osutil.FileExists(filepath.Join(s.cm.CacheDir(), cacheKeys[2])), Equals, true) + c.Check(osutil.FileExists(filepath.Join(s.cm.CacheDir(), cacheKeys[len(cacheKeys)-1])), Equals, true) + +} diff -Nru snapd-2.28.5/store/details.go snapd-2.29.3/store/details.go --- snapd-2.28.5/store/details.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/store/details.go 2017-10-23 06:17:27.000000000 +0000 @@ -34,7 +34,7 @@ Deltas []snapDeltaDetail `json:"deltas,omitempty"` DownloadSize int64 `json:"binary_filesize,omitempty"` DownloadURL string `json:"download_url,omitempty"` - Epoch string `json:"epoch"` + Epoch snap.Epoch `json:"epoch"` IconURL string `json:"icon_url"` LastUpdated string `json:"last_updated,omitempty"` Name string `json:"package_name"` @@ -84,11 +84,11 @@ // channelSnapInfoDetails is the subset of snapDetails we need to get // information about the snaps in the various channels type channelSnapInfoDetails struct { - Revision int `json:"revision"` // store revisions are ints starting at 1 - Confinement string `json:"confinement"` - Version string `json:"version"` - Channel string `json:"channel"` - Epoch string `json:"epoch"` - DownloadSize int64 `json:"binary_filesize"` - Info string `json:"info"` + Revision int `json:"revision"` // store revisions are ints starting at 1 + Confinement string `json:"confinement"` + Version string `json:"version"` + Channel string `json:"channel"` + Epoch snap.Epoch `json:"epoch"` + DownloadSize int64 `json:"binary_filesize"` + Info string `json:"info"` } diff -Nru snapd-2.28.5/store/errors.go snapd-2.29.3/store/errors.go --- snapd-2.28.5/store/errors.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/store/errors.go 2017-10-27 06:13:10.000000000 +0000 @@ -24,8 +24,6 @@ "fmt" "net/url" "strings" - - "github.com/snapcore/snapd/asserts" ) var ( @@ -48,6 +46,8 @@ Err2faFailed = errors.New("two factor authentication failed") // ErrInvalidCredentials is returned on login error + // It can also be returned when refreshing the discharge + // macaroon if the user has changed their password. ErrInvalidCredentials = errors.New("invalid credentials") // ErrTOSNotAccepted is returned when the user has not accepted the store's terms of service. @@ -110,12 +110,3 @@ // (empirically this checks out) return strings.Join(es, " ") } - -// AssertionNotFoundError is returned when an assertion can not be found -type AssertionNotFoundError struct { - Ref *asserts.Ref -} - -func (e *AssertionNotFoundError) Error() string { - return fmt.Sprintf("%v not found", e.Ref) -} diff -Nru snapd-2.28.5/store/export_test.go snapd-2.29.3/store/export_test.go --- snapd-2.28.5/store/export_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/store/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,9 +20,6 @@ package store import ( - "net/url" - "reflect" - "github.com/snapcore/snapd/testutil" "gopkg.in/retry.v1" @@ -37,18 +34,6 @@ }) } -func (cfg *Config) apiURIs() map[string]*url.URL { - urls := map[string]*url.URL{} - - v := reflect.ValueOf(*cfg) - t := reflect.TypeOf(*cfg) - n := v.NumField() - for i := 0; i < n; i++ { - vf := v.Field(i) - if u, ok := vf.Interface().(*url.URL); ok { - urls[t.Field(i).Name] = u - } - } - - return urls +func (cm *CacheManager) CacheDir() string { + return cm.cacheDir } diff -Nru snapd-2.28.5/store/store.go snapd-2.29.3/store/store.go --- snapd-2.28.5/store/store.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/store/store.go 2017-11-02 15:44:20.000000000 +0000 @@ -83,7 +83,7 @@ info.Architectures = d.Architectures info.Type = d.Type info.Version = d.Version - info.Epoch = "0" + info.Epoch = d.Epoch info.RealName = d.Name info.SnapID = d.SnapID info.Revision = snap.R(d.Revision) @@ -165,14 +165,10 @@ // Config represents the configuration to access the snap store type Config struct { - SearchURI *url.URL - DetailsURI *url.URL - BulkURI *url.URL - AssertionsURI *url.URL - OrdersURI *url.URL - BuyURI *url.URL - CustomersMeURI *url.URL - SectionsURI *url.URL + // Store API base URLs. The assertions url is only separate because it can + // be overridden by its own env var. + StoreBaseURL *url.URL + AssertionsBaseURL *url.URL // StoreID is the store id used if we can't get one through the AuthContext. StoreID string @@ -182,11 +178,15 @@ DetailFields []string DeltaFormat string + + // CacheDownloads is the number of downloads that should be cached + CacheDownloads int } -// SetAPI updates API URLs in the Config. Must not be used to change active config. -func (cfg *Config) SetAPI(api *url.URL) error { - storeBaseURI, err := storeURL(api) +// SetBaseURL updates the store API's base URL in the Config. Must not be used +// to change active config. +func (cfg *Config) SetBaseURL(u *url.URL) error { + storeBaseURI, err := storeURL(u) if err != nil { return err } @@ -195,21 +195,8 @@ return err } - // XXX: Repeating "api/" here is cumbersome, but the next generation - // of store APIs will probably drop that prefix (since it now - // duplicates the hostname), and we may want to switch to v2 APIs - // one at a time; so it's better to consider that as part of - // individual endpoint paths. - cfg.SearchURI = urlJoin(storeBaseURI, "api/v1/snaps/search") - // slash at the end because snap name is appended to this with .Parse(snapName) - cfg.DetailsURI = urlJoin(storeBaseURI, "api/v1/snaps/details/") - cfg.BulkURI = urlJoin(storeBaseURI, "api/v1/snaps/metadata") - cfg.SectionsURI = urlJoin(storeBaseURI, "api/v1/snaps/sections") - cfg.OrdersURI = urlJoin(storeBaseURI, "api/v1/snaps/purchases/orders") - cfg.BuyURI = urlJoin(storeBaseURI, "api/v1/snaps/purchases/buy") - cfg.CustomersMeURI = urlJoin(storeBaseURI, "api/v1/snaps/purchases/customers/me") - - cfg.AssertionsURI = urlJoin(assertsBaseURI, "assertions/") + cfg.StoreBaseURL = storeBaseURI + cfg.AssertionsBaseURL = assertsBaseURI return nil } @@ -224,6 +211,13 @@ buyURI *url.URL customersMeURI *url.URL sectionsURI *url.URL + commandsURI *url.URL + + // Device auth endpoints. + // - deviceNonceURI points to endpoint to get a nonce + // - deviceSessionURI points to endpoint to get a device session + deviceNonceURI *url.URL + deviceSessionURI *url.URL architecture string series string @@ -241,6 +235,8 @@ mu sync.Mutex suggestedCurrency string + + cacher downloadCache } func respToError(resp *http.Response, msg string) error { @@ -287,18 +283,17 @@ return osutil.GetenvBool("SNAPPY_USE_STAGING_STORE") } -// Extend a base URL with additional unescaped paths. (url.Parse handles -// resolving relative links, which isn't quite what we want: that goes wrong if -// the base URL doesn't end with a slash.) -func urlJoin(base *url.URL, paths ...string) *url.URL { - if len(paths) == 0 { - return base - } - url := *base - url.RawQuery = "" - paths = append([]string{strings.TrimSuffix(url.Path, "/")}, paths...) - url.Path = strings.Join(paths, "/") - return &url +// Clone a base URL and update with optional path and query. +func endpointURL(base *url.URL, path string, query url.Values) *url.URL { + u := *base + if path != "" { + u.Path = strings.TrimSuffix(u.Path, "/") + "/" + strings.TrimPrefix(path, "/") + u.RawQuery = "" + } + if len(query) != 0 { + u.RawQuery = query.Encode() + } + return &u } // apiURL returns the system default base API URL. @@ -358,9 +353,9 @@ } return u, nil } - // XXX: This will eventually become urlJoin(storeBaseURI, "v2/") + // XXX: This will eventually become endpointURL(storeBaseURI, "v2/", nil) // once new bulk-friendly APIs are designed and implemented. - return urlJoin(storeBaseURI, "api/v1/snaps/"), nil + return endpointURL(storeBaseURI, "api/v1/snaps", nil), nil } func myappsURL() string { @@ -386,7 +381,7 @@ if storeBaseURI.RawQuery != "" { panic("store API URL may not contain query string") } - err = defaultConfig.SetAPI(storeBaseURI) + err = defaultConfig.SetBaseURL(storeBaseURI) if err != nil { panic(err) } @@ -424,32 +419,6 @@ fields = detailFields } - rawQuery := "" - if len(fields) > 0 { - v := url.Values{} - v.Set("fields", strings.Join(fields, ",")) - rawQuery = v.Encode() - } - - var searchURI *url.URL - if cfg.SearchURI != nil { - uri := *cfg.SearchURI - uri.RawQuery = rawQuery - searchURI = &uri - } - - var detailsURI *url.URL - if cfg.DetailsURI != nil { - uri := *cfg.DetailsURI - uri.RawQuery = rawQuery - detailsURI = &uri - } - - var sectionsURI *url.URL - if cfg.SectionsURI != nil { - sectionsURI = cfg.SectionsURI - } - architecture := arch.UbuntuArchitecture() if cfg.Architecture != "" { architecture = cfg.Architecture @@ -465,16 +434,14 @@ deltaFormat = defaultSupportedDeltaFormat } - // see https://wiki.ubuntu.com/AppStore/Interfaces/ClickPackageIndex - return &Store{ - searchURI: searchURI, - detailsURI: detailsURI, - bulkURI: cfg.BulkURI, - assertionsURI: cfg.AssertionsURI, - ordersURI: cfg.OrdersURI, - buyURI: cfg.BuyURI, - customersMeURI: cfg.CustomersMeURI, - sectionsURI: sectionsURI, + var cacher downloadCache + if cfg.CacheDownloads > 0 { + cacher = NewCacheManager(dirs.SnapDownloadCacheDir, cfg.CacheDownloads) + } else { + cacher = &nullCache{} + } + + store := &Store{ series: series, architecture: architecture, noCDN: osutil.GetenvBool("SNAPPY_STORE_NO_CDN"), @@ -482,12 +449,47 @@ detailFields: fields, authContext: authContext, deltaFormat: deltaFormat, + cacher: cacher, client: httputil.NewHTTPClient(&httputil.ClientOpts{ Timeout: 10 * time.Second, MayLogBody: true, }), } + + // see https://wiki.ubuntu.com/AppStore/Interfaces/ClickPackageIndex + // XXX: These are all required in real system but optional makes it + // convenient for tests. + // XXX: Repeating "api/" here is cumbersome, but the next generation + // of store APIs will probably drop that prefix (since it now + // duplicates the hostname), and we may want to switch to v2 APIs + // one at a time; so it's better to consider that as part of + // individual endpoint paths. + if cfg.StoreBaseURL != nil { + store.searchURI = endpointURL(cfg.StoreBaseURL, "api/v1/snaps/search", nil) + store.detailsURI = endpointURL(cfg.StoreBaseURL, "api/v1/snaps/details", nil) + store.bulkURI = endpointURL(cfg.StoreBaseURL, "api/v1/snaps/metadata", nil) + store.ordersURI = endpointURL(cfg.StoreBaseURL, "api/v1/snaps/purchases/orders", nil) + store.buyURI = endpointURL(cfg.StoreBaseURL, "api/v1/snaps/purchases/buy", nil) + store.customersMeURI = endpointURL(cfg.StoreBaseURL, "api/v1/snaps/purchases/customers/me", nil) + store.sectionsURI = endpointURL(cfg.StoreBaseURL, "api/v1/snaps/sections", nil) + store.commandsURI = endpointURL(cfg.StoreBaseURL, "api/v1/snaps/names", nil) + store.deviceNonceURI = endpointURL(cfg.StoreBaseURL, "api/v1/snaps/auth/nonces", nil) + store.deviceSessionURI = endpointURL(cfg.StoreBaseURL, "api/v1/snaps/auth/sessions", nil) + } + if cfg.AssertionsBaseURL != nil { + store.assertionsURI = endpointURL(cfg.AssertionsBaseURL, "assertions", nil) + } + + return store +} + +func (s *Store) defaultSnapQuery() url.Values { + q := url.Values{} + if len(s.detailFields) != 0 { + q.Set("fields", strings.Join(s.detailFields, ",")) + } + return q } // LoginUser logs user in the store and returns the authentication macaroons. @@ -616,7 +618,7 @@ return fmt.Errorf("internal error: no authContext") } - nonce, err := requestStoreDeviceNonce() + nonce, err := requestStoreDeviceNonce(s.deviceNonceURI.String()) if err != nil { return err } @@ -626,7 +628,7 @@ return err } - session, err := requestDeviceSession(devSessReqParams, device.SessionMacaroon) + session, err := requestDeviceSession(s.deviceSessionURI.String(), devSessReqParams, device.SessionMacaroon) if err != nil { return err } @@ -681,6 +683,47 @@ } } +var expectedCatalogPreamble = []interface{}{ + json.Delim('{'), + "_embedded", + json.Delim('{'), + "clickindex:package", + json.Delim('['), +} + +type catalogItem struct { + Name string `json:"package_name"` +} + +func decodeCatalog(resp *http.Response, names io.Writer) error { + const what = "decode new commands catalog" + if resp.StatusCode != 200 { + return respToError(resp, what) + } + dec := json.NewDecoder(resp.Body) + for _, expectedToken := range expectedCatalogPreamble { + token, err := dec.Token() + if err != nil { + return err + } + if token != expectedToken { + return fmt.Errorf(what+": bad catalog preamble: expected %#v, got %#v", expectedToken, token) + } + } + + for dec.More() { + var v catalogItem + if err := dec.Decode(&v); err != nil { + return fmt.Errorf(what+": %v", err) + } + if v.Name != "" { + fmt.Fprintln(names, v.Name) + } + } + + return nil +} + func decodeJSONBody(resp *http.Response, success interface{}, failure interface{}) error { ok := (resp.StatusCode == 200 || resp.StatusCode == 201) // always decode on success; decode failures only if body is not empty @@ -939,12 +982,7 @@ // SnapInfo returns the snap.Info for the store-hosted snap matching the given spec, or an error. func (s *Store) SnapInfo(snapSpec SnapSpec, user *auth.UserState) (*snap.Info, error) { - // get the query before doing Parse, as that overwrites it - query := s.detailsURI.Query() - u, err := s.detailsURI.Parse(snapSpec.Name) - if err != nil { - return nil, err - } + query := s.defaultSnapQuery() channel := snapSpec.Channel var sel string @@ -964,8 +1002,7 @@ } query.Set("channel", channel) - u.RawQuery = query.Encode() - + u := endpointURL(s.detailsURI, snapSpec.Name, query) reqOptions := &requestOptions{ Method: "GET", URL: u, @@ -1030,8 +1067,7 @@ return nil, ErrBadQuery } - u := *s.searchURI // make a copy, so we can mutate it - q := u.Query() + q := s.defaultSnapQuery() if search.Private { if search.Prefix { @@ -1057,11 +1093,11 @@ } else { q.Set("confinement", "strict") } - u.RawQuery = q.Encode() + u := endpointURL(s.searchURI, "", q) reqOptions := &requestOptions{ Method: "GET", - URL: &u, + URL: u, Accept: halJsonContentType, } @@ -1096,15 +1132,9 @@ // Sections retrieves the list of available store sections. func (s *Store) Sections(user *auth.UserState) ([]string, error) { - u := *s.sectionsURI // make a copy, so we can mutate it - - q := u.Query() - - u.RawQuery = q.Encode() - reqOptions := &requestOptions{ Method: "GET", - URL: &u, + URL: s.sectionsURI, Accept: halJsonContentType, } @@ -1130,25 +1160,65 @@ return sectionNames, nil } +// WriteCatalogs queries the "commands" endpoint and writes the +// command names into the given io.Writer. +func (s *Store) WriteCatalogs(names io.Writer) error { + u := *s.commandsURI + + q := u.Query() + if release.OnClassic { + q.Set("confinement", "strict,classic") + } else { + q.Set("confinement", "strict") + } + + u.RawQuery = q.Encode() + reqOptions := &requestOptions{ + Method: "GET", + URL: &u, + Accept: halJsonContentType, + } + + doRequest := func() (*http.Response, error) { + return s.doRequest(context.TODO(), s.client, reqOptions, nil) + } + readResponse := func(resp *http.Response) error { + return decodeCatalog(resp, names) + } + + resp, err := httputil.RetryRequest(u.String(), doRequest, readResponse, defaultRetryStrategy) + if err != nil { + return err + } + if resp.StatusCode != 200 { + return respToError(resp, "refresh commands catalog") + } + + return nil +} + // RefreshCandidate contains information for the store about the currently // installed snap so that the store can decide what update we should see type RefreshCandidate struct { SnapID string Revision snap.Revision - Epoch string + Epoch snap.Epoch Block []snap.Revision // the desired channel Channel string + // whether validation should be ignored + IgnoreValidation bool } // the exact bits that we need to send to the store type currentSnapJSON struct { - SnapID string `json:"snap_id"` - Channel string `json:"channel"` - Revision int `json:"revision,omitempty"` - Epoch string `json:"epoch"` - Confinement string `json:"confinement"` + SnapID string `json:"snap_id"` + Channel string `json:"channel"` + Revision int `json:"revision,omitempty"` + Epoch snap.Epoch `json:"epoch"` + Confinement string `json:"confinement"` + IgnoreValidation bool `json:"ignore_validation,omitempty"` } type metadataWrapper struct { @@ -1176,10 +1246,11 @@ } return ¤tSnapJSON{ - SnapID: cs.SnapID, - Channel: channel, - Epoch: cs.Epoch, - Revision: cs.Revision.N, + SnapID: cs.SnapID, + Channel: channel, + Epoch: cs.Epoch, + Revision: cs.Revision.N, + IgnoreValidation: cs.IgnoreValidation, // confinement purposely left empty } } @@ -1320,6 +1391,11 @@ if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil { return err } + + if err := s.cacher.Get(downloadInfo.Sha3_384, targetPath); err == nil { + return nil + } + if useDeltas() { logger.Debugf("Available deltas returned by store: %v", downloadInfo.Deltas) @@ -1399,7 +1475,11 @@ return err } - return w.Sync() + if err := w.Sync(); err != nil { + return err + } + + return s.cacher.Put(downloadInfo.Sha3_384, targetPath) } // download writes an http.Request showing a progress.Meter @@ -1470,7 +1550,7 @@ } if pbar == nil { - pbar = &progress.NullProgress{} + pbar = progress.Null } pbar.Start(name, float64(resp.ContentLength)) mw := io.MultiWriter(w, h, pbar) @@ -1626,13 +1706,9 @@ // Assertion retrivies the assertion for the given type and primary key. func (s *Store) Assertion(assertType *asserts.AssertionType, primaryKey []string, user *auth.UserState) (asserts.Assertion, error) { - u, err := s.assertionsURI.Parse(path.Join(assertType.Name, path.Join(primaryKey...))) - if err != nil { - return nil, err - } v := url.Values{} v.Set("max-format", strconv.Itoa(assertType.MaxSupportedFormat())) - u.RawQuery = v.Encode() + u := endpointURL(s.assertionsURI, path.Join(assertType.Name, path.Join(primaryKey...)), v) reqOptions := &requestOptions{ Method: "GET", @@ -1659,7 +1735,12 @@ return fmt.Errorf("cannot decode assertion service error with HTTP status code %d: %v", resp.StatusCode, e) } if svcErr.Status == 404 { - return &AssertionNotFoundError{&asserts.Ref{Type: assertType, PrimaryKey: primaryKey}} + // best-effort + headers, _ := asserts.HeadersFromPrimaryKey(assertType, primaryKey) + return &asserts.NotFoundError{ + Type: assertType, + Headers: headers, + } } return fmt.Errorf("assertion service error: [%s] %q", svcErr.Title, svcErr.Detail) } diff -Nru snapd-2.28.5/store/storetest/storetest.go snapd-2.29.3/store/storetest/storetest.go --- snapd-2.28.5/store/storetest/storetest.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/store/storetest/storetest.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,11 +20,13 @@ package storetest import ( + "io" + "golang.org/x/net/context" "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/overlord/auth" - "github.com/snapcore/snapd/overlord/snapstate" + "github.com/snapcore/snapd/overlord/storestate" "github.com/snapcore/snapd/progress" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/store" @@ -37,7 +39,7 @@ type Store struct{} // ensure we conform -var _ snapstate.StoreService = Store{} +var _ storestate.StoreService = Store{} func (Store) SnapInfo(store.SnapSpec, *auth.UserState) (*snap.Info, error) { panic("Store.SnapInfo not expected") @@ -79,6 +81,6 @@ panic("Store.Assertion not expected") } -func (Store) SnapCommands() (map[string][]string, error) { - panic("fakeStore.SnapCommands not expected") +func (Store) WriteCatalogs(io.Writer) error { + panic("fakeStore.WriteCatalogs not expected") } diff -Nru snapd-2.28.5/store/store_test.go snapd-2.29.3/store/store_test.go --- snapd-2.28.5/store/store_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/store/store_test.go 2017-11-02 15:44:20.000000000 +0000 @@ -32,6 +32,7 @@ "net/url" "os" "path/filepath" + "regexp" "strings" "testing" "time" @@ -61,68 +62,91 @@ var _ = Suite(&configTestSuite{}) -func (suite *configTestSuite) TestSetAPI(c *C) { +func (suite *configTestSuite) TestSetBaseURL(c *C) { // Sanity check to prove at least one URI changes. cfg := DefaultConfig() - c.Assert(cfg.SectionsURI.Scheme, Equals, "https") - c.Assert(cfg.SectionsURI.Host, Equals, "api.snapcraft.io") - c.Assert(cfg.SectionsURI.Path, Matches, "/api/v1/snaps/.*") + c.Assert(cfg.StoreBaseURL.String(), Equals, "https://api.snapcraft.io/") - api, err := url.Parse("http://example.com/path/prefix/") + u, err := url.Parse("http://example.com/path/prefix/") c.Assert(err, IsNil) - err = cfg.SetAPI(api) + err = cfg.SetBaseURL(u) c.Assert(err, IsNil) - for _, uri := range cfg.apiURIs() { - c.Assert(uri, NotNil) - c.Check(uri.String(), Matches, "http://example.com/path/prefix/api/v1/snaps/.*") - } + c.Check(cfg.StoreBaseURL.String(), Equals, "http://example.com/path/prefix/") + c.Check(cfg.AssertionsBaseURL.String(), Equals, "http://example.com/path/prefix/api/v1/snaps") } -func (suite *configTestSuite) TestSetAPIStoreOverrides(c *C) { +func (suite *configTestSuite) TestSetBaseURLStoreOverrides(c *C) { cfg := DefaultConfig() - c.Assert(cfg.SetAPI(apiURL()), IsNil) - c.Check(cfg.SearchURI, Matches, apiURL().String()+".*") + c.Assert(cfg.SetBaseURL(apiURL()), IsNil) + c.Check(cfg.StoreBaseURL, Matches, apiURL().String()+".*") c.Assert(os.Setenv("SNAPPY_FORCE_API_URL", "https://force-api.local/"), IsNil) defer os.Setenv("SNAPPY_FORCE_API_URL", "") cfg = DefaultConfig() - c.Assert(cfg.SetAPI(apiURL()), IsNil) - for _, u := range cfg.apiURIs() { - c.Check(u.String(), Matches, "https://force-api.local/.*") - } + c.Assert(cfg.SetBaseURL(apiURL()), IsNil) + c.Check(cfg.StoreBaseURL.String(), Equals, "https://force-api.local/") + c.Check(cfg.AssertionsBaseURL.String(), Equals, "https://force-api.local/api/v1/snaps") } -func (suite *configTestSuite) TestSetAPIStoreURLBadEnviron(c *C) { +func (suite *configTestSuite) TestSetBaseURLStoreURLBadEnviron(c *C) { c.Assert(os.Setenv("SNAPPY_FORCE_API_URL", "://example.com"), IsNil) defer os.Setenv("SNAPPY_FORCE_API_URL", "") cfg := DefaultConfig() - err := cfg.SetAPI(apiURL()) + err := cfg.SetBaseURL(apiURL()) c.Check(err, ErrorMatches, "invalid SNAPPY_FORCE_API_URL: parse ://example.com: missing protocol scheme") } -func (suite *configTestSuite) TestSetAPIAssertsOverrides(c *C) { +func (suite *configTestSuite) TestSetBaseURLAssertsOverrides(c *C) { cfg := DefaultConfig() - c.Assert(cfg.SetAPI(apiURL()), IsNil) - c.Check(cfg.SearchURI, Matches, apiURL().String()+".*") + c.Assert(cfg.SetBaseURL(apiURL()), IsNil) + c.Check(cfg.AssertionsBaseURL, Matches, apiURL().String()+".*") c.Assert(os.Setenv("SNAPPY_FORCE_SAS_URL", "https://force-sas.local/"), IsNil) defer os.Setenv("SNAPPY_FORCE_SAS_URL", "") cfg = DefaultConfig() - c.Assert(cfg.SetAPI(apiURL()), IsNil) - c.Check(cfg.AssertionsURI, Matches, "https://force-sas.local/.*") + c.Assert(cfg.SetBaseURL(apiURL()), IsNil) + c.Check(cfg.AssertionsBaseURL, Matches, "https://force-sas.local/.*") } -func (suite *configTestSuite) TestSetAPIAssertsURLBadEnviron(c *C) { +func (suite *configTestSuite) TestSetBaseURLAssertsURLBadEnviron(c *C) { c.Assert(os.Setenv("SNAPPY_FORCE_SAS_URL", "://example.com"), IsNil) defer os.Setenv("SNAPPY_FORCE_SAS_URL", "") cfg := DefaultConfig() - err := cfg.SetAPI(apiURL()) + err := cfg.SetBaseURL(apiURL()) c.Check(err, ErrorMatches, "invalid SNAPPY_FORCE_SAS_URL: parse ://example.com: missing protocol scheme") } +const ( + // Store API paths/patterns. + authNoncesPath = "/api/v1/snaps/auth/nonces" + authSessionPath = "/api/v1/snaps/auth/sessions" + buyPath = "/api/v1/snaps/purchases/buy" + customersMePath = "/api/v1/snaps/purchases/customers/me" + detailsPathPattern = "/api/v1/snaps/details/.*" + metadataPath = "/api/v1/snaps/metadata" + ordersPath = "/api/v1/snaps/purchases/orders" + searchPath = "/api/v1/snaps/search" + sectionsPath = "/api/v1/snaps/sections" +) + +// Build details path for a snap name. +func detailsPath(snapName string) string { + return strings.Replace(detailsPathPattern, ".*", snapName, 1) +} + +// Assert that a request is roughly as expected. Useful in fakes that should +// only attempt to handle a specific request. +func assertRequest(c *C, r *http.Request, method, pathPattern string) { + pathMatch, err := regexp.MatchString("^"+pathPattern+"$", r.URL.Path) + c.Assert(err, IsNil) + if r.Method != method && pathMatch { + c.Fatalf("request didn't match (expected %s %s, got %s %s)", method, pathPattern, r.Method, r.URL.Path) + } +} + type remoteRepoTestSuite struct { testutil.BaseTest store *Store @@ -133,6 +157,8 @@ origDownloadFunc func(context.Context, string, string, string, *auth.UserState, *Store, io.ReadWriteSeeker, int64, progress.Meter) error mockXDelta *testutil.MockCmd + + restoreLogger func() } var _ = Suite(&remoteRepoTestSuite{}) @@ -314,10 +340,7 @@ os.Setenv("SNAPD_DEBUG", "1") t.AddCleanup(func() { os.Unsetenv("SNAPD_DEBUG") }) - t.logbuf = bytes.NewBuffer(nil) - l, err := logger.New(t.logbuf, logger.DefaultFlags) - c.Assert(err, IsNil) - logger.SetLogger(l) + t.logbuf, t.restoreLogger = logger.MockLogger() root, err := makeTestMacaroon() c.Assert(err, IsNil) @@ -344,10 +367,7 @@ func (t *remoteRepoTestSuite) TearDownTest(c *C) { download = t.origDownloadFunc t.mockXDelta.Restore() -} - -func (t *remoteRepoTestSuite) TearDownSuite(c *C) { - logger.SimpleSetup() + t.restoreLogger() } func (t *remoteRepoTestSuite) expectedAuthorization(c *C, user *auth.UserState) string { @@ -1516,6 +1536,42 @@ c.Check(refreshDischargeEndpointHit, Equals, true) } +func (t *remoteRepoTestSuite) TestDoRequestForwardsRefreshAuthFailure(c *C) { + // mock refresh response + refreshDischargeEndpointHit := false + mockSSOServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(mockStoreInvalidLoginCode) + io.WriteString(w, mockStoreInvalidLogin) + refreshDischargeEndpointHit = true + })) + defer mockSSOServer.Close() + UbuntuoneRefreshDischargeAPI = mockSSOServer.URL + "/tokens/refresh" + + // mock store response (requiring auth refresh) + mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c.Check(r.UserAgent(), Equals, userAgent) + + authorization := r.Header.Get("Authorization") + c.Check(authorization, Equals, t.expectedAuthorization(c, t.user)) + w.Header().Set("WWW-Authenticate", "Macaroon needs_refresh=1") + w.WriteHeader(401) + })) + c.Assert(mockServer, NotNil) + defer mockServer.Close() + + authContext := &testAuthContext{c: c, device: t.device, user: t.user} + repo := New(&Config{}, authContext) + c.Assert(repo, NotNil) + + endpoint, _ := url.Parse(mockServer.URL) + reqOptions := &requestOptions{Method: "GET", URL: endpoint} + + response, err := repo.doRequest(context.TODO(), repo.client, reqOptions, t.user) + c.Assert(err, Equals, ErrInvalidCredentials) + c.Check(response, IsNil) + c.Check(refreshDischargeEndpointHit, Equals, true) +} + func (t *remoteRepoTestSuite) TestDoRequestSetsAndRefreshesDeviceAuth(c *C) { deviceSessionRequested := false refreshSessionRequested := false @@ -1536,9 +1592,9 @@ c.Check(authorization, Equals, `Macaroon root="refreshed-session-macaroon"`) io.WriteString(w, "response-data") } - case "/api/v1/auth/nonces": + case authNoncesPath: io.WriteString(w, `{"nonce": "1234567890:9876543210"}`) - case "/api/v1/auth/sessions": + case authSessionPath: // sanity of request jsonReq, err := ioutil.ReadAll(r.Body) c.Assert(err, IsNil) @@ -1565,21 +1621,21 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - DeviceNonceAPI = mockServer.URL + "/api/v1/auth/nonces" - DeviceSessionAPI = mockServer.URL + "/api/v1/auth/sessions" + mockServerURL, _ := url.Parse(mockServer.URL) // make sure device session is not set t.device.SessionMacaroon = "" authContext := &testAuthContext{c: c, device: t.device, user: t.user} - repo := New(&Config{}, authContext) + repo := New(&Config{ + StoreBaseURL: mockServerURL, + }, authContext) c.Assert(repo, NotNil) - endpoint, _ := url.Parse(mockServer.URL) - reqOptions := &requestOptions{Method: "GET", URL: endpoint} + reqOptions := &requestOptions{Method: "GET", URL: mockServerURL} response, err := repo.doRequest(context.TODO(), repo.client, reqOptions, t.user) - defer response.Body.Close() c.Assert(err, IsNil) + defer response.Body.Close() responseData, err := ioutil.ReadAll(response.Body) c.Assert(err, IsNil) @@ -1884,6 +1940,7 @@ restore := release.MockOnClassic(false) defer restore() mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", detailsPathPattern) c.Check(r.UserAgent(), Equals, userAgent) // check device authorization is set, implicitly checking doRequest was used @@ -1893,7 +1950,7 @@ storeID := r.Header.Get("X-Ubuntu-Store") c.Check(storeID, Equals, "") - c.Check(r.URL.Path, Equals, "/details/hello-world") + c.Check(r.URL.Path, Matches, ".*/hello-world") c.Check(r.URL.Query().Get("channel"), Equals, "edge") c.Check(r.URL.Query().Get("fields"), Equals, "abc,def") @@ -1913,10 +1970,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - detailsURI, err := url.Parse(mockServer.URL + "/details/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - DetailsURI: detailsURI, + StoreBaseURL: mockServerURL, DetailFields: []string{"abc", "def"}, } authContext := &testAuthContext{c: c, device: t.device} @@ -1954,7 +2010,7 @@ c.Check(result.Contact, Equals, "mailto:snappy-devel@lists.ubuntu.com") // Make sure the epoch (currently not sent by the store) defaults to "0" - c.Check(result.Epoch, Equals, "0") + c.Check(result.Epoch.String(), Equals, "0") c.Check(repo.SuggestedCurrency(), Equals, "GBP") @@ -1968,7 +2024,8 @@ restore := release.MockOnClassic(false) defer restore() mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - c.Check(r.URL.Path, Equals, "/details/hello-world") + assertRequest(c, r, "GET", detailsPathPattern) + c.Check(r.URL.Path, Matches, ".*/hello-world") c.Check(r.URL.Query().Get("channel"), Equals, "stable") w.WriteHeader(200) @@ -1979,10 +2036,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - detailsURI, err := url.Parse(mockServer.URL + "/details/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - DetailsURI: detailsURI, + StoreBaseURL: mockServerURL, DetailFields: []string{"abc", "def"}, } authContext := &testAuthContext{c: c, device: t.device} @@ -2003,6 +2059,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryDetails500(c *C) { var n = 0 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", detailsPathPattern) n++ w.WriteHeader(500) })) @@ -2010,10 +2067,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - detailsURI, err := url.Parse(mockServer.URL + "/details/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - DetailsURI: detailsURI, + StoreBaseURL: mockServerURL, DetailFields: []string{}, } authContext := &testAuthContext{c: c, device: t.device} @@ -2026,7 +2082,7 @@ Channel: "edge", Revision: snap.R(0), } - _, err = repo.SnapInfo(spec, nil) + _, err := repo.SnapInfo(spec, nil) c.Assert(err, NotNil) c.Assert(err, ErrorMatches, `cannot get details for snap "hello-world" in channel "edge": got unexpected HTTP status code 500 via GET to "http://.*?/details/hello-world\?channel=edge"`) c.Assert(n, Equals, 5) @@ -2035,6 +2091,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryDetails500once(c *C) { var n = 0 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", detailsPathPattern) n++ if n > 1 { w.Header().Set("X-Suggested-Currency", "GBP") @@ -2048,10 +2105,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - detailsURI, err := url.Parse(mockServer.URL + "/details/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - DetailsURI: detailsURI, + StoreBaseURL: mockServerURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) @@ -2075,9 +2131,10 @@ n := 0 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", detailsPathPattern) switch n { case 0: - c.Check(r.URL.Path, Equals, "/details/hello-world") + c.Check(r.URL.Path, Matches, ".*/hello-world") c.Check(r.URL.Query().Get("channel"), Equals, "") w.Header().Set("X-Suggested-Currency", "GBP") w.WriteHeader(200) @@ -2092,13 +2149,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - detailsURI, err := url.Parse(mockServer.URL + "/details/") - c.Assert(err, IsNil) - bulkURI, err := url.Parse(mockServer.URL + "/metadata") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - DetailsURI: detailsURI, - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) @@ -2120,7 +2173,7 @@ Confinement: snap.StrictConfinement, Channel: "stable", Size: 12345, - Epoch: "0", + Epoch: *snap.E("0"), }, "latest/candidate": { Revision: snap.R(2), @@ -2128,7 +2181,7 @@ Confinement: snap.StrictConfinement, Channel: "candidate", Size: 12345, - Epoch: "0", + Epoch: *snap.E("0"), }, "latest/beta": { Revision: snap.R(8), @@ -2136,7 +2189,7 @@ Confinement: snap.DevModeConfinement, Channel: "beta", Size: 12345, - Epoch: "0", + Epoch: *snap.E("0"), }, "latest/edge": { Revision: snap.R(9), @@ -2144,7 +2197,7 @@ Confinement: snap.DevModeConfinement, Channel: "edge", Size: 12345, - Epoch: "0", + Epoch: *snap.E("0"), }, }) @@ -2158,10 +2211,11 @@ defer os.Unsetenv("SNAPPY_STORE_NO_CDN") mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", detailsPathPattern) storeID := r.Header.Get("X-Ubuntu-Store") c.Check(storeID, Equals, "foo") - c.Check(r.URL.Path, Equals, "/details/hello-world") + c.Check(r.URL.Path, Matches, ".*/details/hello-world") c.Check(r.URL.Query().Get("channel"), Equals, "edge") @@ -2177,10 +2231,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - detailsURI, err := url.Parse(mockServer.URL + "/details/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := DefaultConfig() - cfg.DetailsURI = detailsURI + cfg.StoreBaseURL = mockServerURL cfg.Series = "21" cfg.Architecture = "archXYZ" cfg.StoreID = "foo" @@ -2200,6 +2253,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryStoreIDFromAuthContext(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", detailsPathPattern) storeID := r.Header.Get("X-Ubuntu-Store") c.Check(storeID, Equals, "my-brand-store-id") @@ -2210,10 +2264,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - detailsURI, err := url.Parse(mockServer.URL + "/details/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := DefaultConfig() - cfg.DetailsURI = detailsURI + cfg.StoreBaseURL = mockServerURL cfg.Series = "21" cfg.Architecture = "archXYZ" cfg.StoreID = "fallback" @@ -2233,35 +2286,26 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryRevision(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if strings.HasPrefix(r.URL.Path, ordersPath) { + switch r.URL.Path { + case ordersPath: w.WriteHeader(404) - return + case detailsPath("hello-world"): + c.Check(r.URL.Query(), DeepEquals, url.Values{ + "channel": []string{""}, + "revision": []string{"26"}, + }) + w.WriteHeader(200) + io.WriteString(w, MockDetailsJSON) + default: + c.Fatalf("unexpected request to %q", r.URL.Path) } - c.Check(r.URL.Path, Equals, "/details/hello-world") - c.Check(r.URL.Query(), DeepEquals, url.Values{ - "channel": []string{""}, - "revision": []string{"26"}, - }) - - w.WriteHeader(200) - io.WriteString(w, MockDetailsJSON) })) c.Assert(mockServer, NotNil) defer mockServer.Close() - mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(404) - })) - c.Assert(mockPurchasesServer, NotNil) - defer mockPurchasesServer.Close() - - ordersURI, err := url.Parse(mockPurchasesServer.URL + ordersPath) - c.Assert(err, IsNil) - detailsURI, err := url.Parse(mockServer.URL + "/details/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := DefaultConfig() - cfg.DetailsURI = detailsURI - cfg.OrdersURI = ordersURI + cfg.StoreBaseURL = mockServerURL cfg.DetailFields = []string{} repo := New(cfg, nil) c.Assert(repo, NotNil) @@ -2280,7 +2324,8 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryDetailsOopses(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - c.Check(r.URL.Path, Equals, "/details/hello-world") + assertRequest(c, r, "GET", detailsPathPattern) + c.Check(r.URL.Path, Matches, ".*/hello-world") c.Check(r.URL.Query().Get("channel"), Equals, "edge") w.Header().Set("X-Oops-Id", "OOPS-d4f46f75a5bcc10edcacc87e1fc0119f") @@ -2292,10 +2337,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - detailsURI, err := url.Parse(mockServer.URL + "/details/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - DetailsURI: detailsURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, nil) c.Assert(repo, NotNil) @@ -2306,7 +2350,7 @@ Channel: "edge", Revision: snap.R(0), } - _, err = repo.SnapInfo(spec, nil) + _, err := repo.SnapInfo(spec, nil) c.Assert(err, ErrorMatches, `cannot get details for snap "hello-world" in channel "edge": got unexpected HTTP status code 5.. via GET to "http://\S+" \[OOPS-[[:xdigit:]]*\]`) } @@ -2330,7 +2374,8 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryNoDetails(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - c.Check(r.URL.Path, Equals, "/details/no-such-pkg") + assertRequest(c, r, "GET", detailsPathPattern) + c.Check(r.URL.Path, Matches, ".*/no-such-pkg") q := r.URL.Query() c.Check(q.Get("channel"), Equals, "edge") @@ -2341,10 +2386,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - detailsURI, err := url.Parse(mockServer.URL + "/details/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - DetailsURI: detailsURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, nil) c.Assert(repo, NotNil) @@ -2421,6 +2465,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreFindQueries(c *C) { n := 0 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", searchPath) // check device authorization is set, implicitly checking doRequest was used c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) @@ -2430,7 +2475,7 @@ q := query.Get("q") section := query.Get("section") - c.Check(r.URL.Path, Equals, "/search") + c.Check(r.URL.Path, Matches, ".*/search") c.Check(query.Get("fields"), Equals, "abc,def") // write dummy json so that Find doesn't re-try due to json decoder EOF error @@ -2462,12 +2507,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - serverURL, _ := url.Parse(mockServer.URL) - searchURI, _ := serverURL.Parse("/search") - detailsURI, _ := serverURL.Parse("/details/") + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - DetailsURI: detailsURI, - SearchURI: searchURI, + StoreBaseURL: mockServerURL, DetailFields: []string{"abc", "def"}, } authContext := &testAuthContext{c: c, device: t.device} @@ -2509,9 +2551,10 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreSectionsQuery(c *C) { n := 0 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", sectionsPath) switch n { case 0: - c.Check(r.URL.Path, Equals, "/snaps/sections") + // All good. default: c.Fatalf("what? %d", n) } @@ -2525,9 +2568,8 @@ defer mockServer.Close() serverURL, _ := url.Parse(mockServer.URL) - searchSectionsURI, _ := serverURL.Parse("/snaps/sections") cfg := Config{ - SectionsURI: searchSectionsURI, + StoreBaseURL: serverURL, } repo := New(&cfg, nil) c.Assert(repo, NotNil) @@ -2537,9 +2579,81 @@ c.Check(sections, DeepEquals, []string{"featured", "database"}) } +const mockNamesJSON = ` +{ + "_embedded": { + "clickindex:package": [ + { + "aliases": [ + { + "name": "potato", + "target": "baz" + }, + { + "name": "meh", + "target": "baz" + } + ], + "package_name": "bar" + }, + { + "aliases": null, + "package_name": "foo" + } + ] + } +}` + +func (t *remoteRepoTestSuite) TestUbuntuStoreSnapCommandsOnClassic(c *C) { + t.testUbuntuStoreSnapCommands(c, true) +} + +func (t *remoteRepoTestSuite) TestUbuntuStoreSnapCommandsOnCore(c *C) { + t.testUbuntuStoreSnapCommands(c, false) +} + +func (t *remoteRepoTestSuite) testUbuntuStoreSnapCommands(c *C, onClassic bool) { + defer release.MockOnClassic(onClassic)() + + n := 0 + mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch n { + case 0: + query := r.URL.Query() + c.Check(query, HasLen, 1) + expectedConfinement := "strict" + if onClassic { + expectedConfinement = "strict,classic" + } + c.Check(query.Get("confinement"), Equals, expectedConfinement) + c.Check(r.URL.Path, Equals, "/api/v1/snaps/names") + default: + c.Fatalf("what? %d", n) + } + + w.Header().Set("Content-Type", "application/hal+json") + w.Header().Set("Content-Length", fmt.Sprint(len(mockNamesJSON))) + w.WriteHeader(200) + io.WriteString(w, mockNamesJSON) + n++ + })) + c.Assert(mockServer, NotNil) + defer mockServer.Close() + + serverURL, _ := url.Parse(mockServer.URL) + repo := New(&Config{StoreBaseURL: serverURL}, nil) + c.Assert(repo, NotNil) + + var bufNames bytes.Buffer + err := repo.WriteCatalogs(&bufNames) + c.Check(err, IsNil) + c.Check(bufNames.String(), Equals, "bar\nfoo\n") +} + func (t *remoteRepoTestSuite) TestUbuntuStoreFindPrivate(c *C) { n := 0 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", searchPath) query := r.URL.Query() name := query.Get("name") @@ -2547,7 +2661,7 @@ switch n { case 0: - c.Check(r.URL.Path, Equals, "/search") + c.Check(r.URL.Path, Matches, ".*/search") c.Check(name, Equals, "") c.Check(q, Equals, "foo") c.Check(query.Get("private"), Equals, "true") @@ -2565,9 +2679,8 @@ defer mockServer.Close() serverURL, _ := url.Parse(mockServer.URL) - searchURI, _ := serverURL.Parse("/search") cfg := Config{ - SearchURI: searchURI, + StoreBaseURL: serverURL, } repo := New(&cfg, nil) c.Assert(repo, NotNil) @@ -2583,7 +2696,7 @@ } func (t *remoteRepoTestSuite) TestUbuntuStoreFindFailures(c *C) { - repo := New(&Config{SearchURI: new(url.URL)}, nil) + repo := New(&Config{StoreBaseURL: new(url.URL)}, nil) _, err := repo.Find(&Search{Query: "foo:bar"}, nil) c.Check(err, Equals, ErrBadQuery) _, err = repo.Find(&Search{Query: "foo", Private: true, Prefix: true}, t.user) @@ -2592,16 +2705,16 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreFindFails(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", searchPath) c.Check(r.URL.Query().Get("q"), Equals, "hello") http.Error(w, http.StatusText(418), 418) // I'm a teapot })) c.Assert(mockServer, NotNil) defer mockServer.Close() - searchURI, err := url.Parse(mockServer.URL) - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - SearchURI: searchURI, + StoreBaseURL: mockServerURL, DetailFields: []string{}, // make the error less noisy } repo := New(&cfg, nil) @@ -2614,16 +2727,16 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreFindBadContentType(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", searchPath) c.Check(r.URL.Query().Get("q"), Equals, "hello") io.WriteString(w, MockSearchJSON) })) c.Assert(mockServer, NotNil) defer mockServer.Close() - searchURI, err := url.Parse(mockServer.URL) - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - SearchURI: searchURI, + StoreBaseURL: mockServerURL, DetailFields: []string{}, // make the error less noisy } repo := New(&cfg, nil) @@ -2636,6 +2749,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreFindBadBody(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", searchPath) query := r.URL.Query() c.Check(query.Get("q"), Equals, "hello") w.Header().Set("Content-Type", "application/hal+json") @@ -2644,10 +2758,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - searchURI, err := url.Parse(mockServer.URL) - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - SearchURI: searchURI, + StoreBaseURL: mockServerURL, DetailFields: []string{}, // make the error less noisy } repo := New(&cfg, nil) @@ -2661,22 +2774,22 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreFind500(c *C) { var n = 0 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", searchPath) n++ w.WriteHeader(500) })) c.Assert(mockServer, NotNil) defer mockServer.Close() - searchURI, err := url.Parse(mockServer.URL) - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - SearchURI: searchURI, + StoreBaseURL: mockServerURL, DetailFields: []string{}, } repo := New(&cfg, nil) c.Assert(repo, NotNil) - _, err = repo.Find(&Search{Query: "hello"}, nil) + _, err := repo.Find(&Search{Query: "hello"}, nil) c.Check(err, ErrorMatches, `cannot search: got unexpected HTTP status code 500 via GET to "http://\S+[?&]q=hello.*"`) c.Assert(n, Equals, 5) } @@ -2684,6 +2797,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreFind500once(c *C) { var n = 0 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", searchPath) n++ if n == 1 { w.WriteHeader(500) @@ -2696,10 +2810,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - searchURI, err := url.Parse(mockServer.URL) - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - SearchURI: searchURI, + StoreBaseURL: mockServerURL, DetailFields: []string{}, } repo := New(&cfg, nil) @@ -2713,40 +2826,37 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreFindAuthFailed(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // check authorization is set - authorization := r.Header.Get("Authorization") - c.Check(authorization, Equals, t.expectedAuthorization(c, t.user)) - - query := r.URL.Query() - c.Check(query.Get("q"), Equals, "foo") - if release.OnClassic { - c.Check(query.Get("confinement"), Matches, `strict,classic|classic,strict`) - } else { - c.Check(query.Get("confinement"), Equals, "strict") + switch r.URL.Path { + case searchPath: + // check authorization is set + authorization := r.Header.Get("Authorization") + c.Check(authorization, Equals, t.expectedAuthorization(c, t.user)) + + query := r.URL.Query() + c.Check(query.Get("q"), Equals, "foo") + if release.OnClassic { + c.Check(query.Get("confinement"), Matches, `strict,classic|classic,strict`) + } else { + c.Check(query.Get("confinement"), Equals, "strict") + } + w.Header().Set("Content-Type", "application/hal+json") + io.WriteString(w, MockSearchJSON) + case ordersPath: + c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user)) + c.Check(r.Header.Get("Accept"), Equals, jsonContentType) + c.Check(r.URL.Path, Equals, ordersPath) + w.WriteHeader(401) + io.WriteString(w, "{}") + default: + c.Fatalf("unexpected query %s %s", r.Method, r.URL.Path) } - w.Header().Set("Content-Type", "application/hal+json") - io.WriteString(w, MockSearchJSON) })) c.Assert(mockServer, NotNil) defer mockServer.Close() - mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user)) - c.Check(r.Header.Get("Accept"), Equals, jsonContentType) - c.Check(r.URL.Path, Equals, ordersPath) - w.WriteHeader(401) - io.WriteString(w, "{}") - })) - c.Assert(mockPurchasesServer, NotNil) - defer mockPurchasesServer.Close() - - searchURI, err := url.Parse(mockServer.URL) - c.Assert(err, IsNil) - ordersURI, err := url.Parse(mockPurchasesServer.URL + ordersPath) - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - SearchURI: searchURI, - OrdersURI: ordersURI, + StoreBaseURL: mockServerURL, DetailFields: []string{}, // make the error less noisy } repo := New(&cfg, nil) @@ -2770,14 +2880,33 @@ SnapID: helloWorldSnapID, Channel: "stable", Revision: snap.R(1), - Epoch: "1", + Epoch: *snap.E("1"), + } + cs := currentSnap(cand) + c.Assert(cs, NotNil) + c.Check(cs.SnapID, Equals, cand.SnapID) + c.Check(cs.Channel, Equals, cand.Channel) + c.Check(cs.Epoch, DeepEquals, cand.Epoch) + c.Check(cs.Revision, Equals, cand.Revision.N) + c.Check(cs.IgnoreValidation, Equals, cand.IgnoreValidation) + c.Check(t.logbuf.String(), Equals, "") +} + +func (t *remoteRepoTestSuite) TestCurrentSnapIgnoreValidation(c *C) { + cand := &RefreshCandidate{ + SnapID: helloWorldSnapID, + Channel: "stable", + Revision: snap.R(1), + Epoch: *snap.E("1"), + IgnoreValidation: true, } cs := currentSnap(cand) c.Assert(cs, NotNil) c.Check(cs.SnapID, Equals, cand.SnapID) c.Check(cs.Channel, Equals, cand.Channel) - c.Check(cs.Epoch, Equals, cand.Epoch) + c.Check(cs.Epoch, DeepEquals, cand.Epoch) c.Check(cs.Revision, Equals, cand.Revision.N) + c.Check(cs.IgnoreValidation, Equals, cand.IgnoreValidation) c.Check(t.logbuf.String(), Equals, "") } @@ -2785,13 +2914,13 @@ cand := &RefreshCandidate{ SnapID: helloWorldSnapID, Revision: snap.R(1), - Epoch: "1", + Epoch: *snap.E("1"), } cs := currentSnap(cand) c.Assert(cs, NotNil) c.Check(cs.SnapID, Equals, cand.SnapID) c.Check(cs.Channel, Equals, "stable") - c.Check(cs.Epoch, Equals, cand.Epoch) + c.Check(cs.Epoch, DeepEquals, cand.Epoch) c.Check(cs.Revision, Equals, cand.Revision.N) c.Check(t.logbuf.String(), Equals, "") } @@ -2869,6 +2998,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryRefreshForCandidates(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) // check device authorization is set, implicitly checking doRequest was used c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) @@ -2898,10 +3028,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) @@ -2912,7 +3041,6 @@ SnapID: helloWorldSnapID, Channel: "stable", Revision: 1, - Epoch: "0", }, }, nil) @@ -2930,6 +3058,7 @@ n := 0 var mockServer *httptest.Server mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) n++ if n < 4 { io.WriteString(w, "{") @@ -2949,10 +3078,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) @@ -2962,7 +3090,6 @@ SnapID: helloWorldSnapID, Channel: "stable", Revision: 1, - Epoch: "0", }}, nil) c.Assert(err, IsNil) c.Assert(n, Equals, 4) @@ -2984,7 +3111,7 @@ SnapID: helloWorldSnapID, Channel: "stable", Revision: 1, - Epoch: "0", + Epoch: *snap.E("0"), }}) return []*snapDetails{{ Name: "hello-world", @@ -3002,7 +3129,7 @@ SnapID: helloWorldSnapID, Channel: "stable", Revision: snap.R(1), - Epoch: "0", + Epoch: *snap.E("0"), }, nil) c.Assert(err, IsNil) c.Assert(result.Name(), Equals, "hello-world") @@ -3013,6 +3140,40 @@ c.Assert(result.Deltas, HasLen, 0) } +func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryLookupRefreshIgnoreValidation(c *C) { + defer mockRFC(func(_ *Store, currentSnaps []*currentSnapJSON, _ *auth.UserState) ([]*snapDetails, error) { + c.Check(currentSnaps, DeepEquals, []*currentSnapJSON{{ + SnapID: helloWorldSnapID, + Channel: "stable", + Revision: 1, + Epoch: *snap.E("0"), + IgnoreValidation: true, + }}) + return []*snapDetails{{ + Name: "hello-world", + Revision: 26, + Version: "6.1", + SnapID: helloWorldSnapID, + DeveloperID: helloWorldDeveloperID, + }}, nil + })() + + repo := New(nil, &testAuthContext{c: c, device: t.device}) + c.Assert(repo, NotNil) + + result, err := repo.LookupRefresh(&RefreshCandidate{ + SnapID: helloWorldSnapID, + Channel: "stable", + Revision: snap.R(1), + Epoch: *snap.E("0"), + IgnoreValidation: true, + }, nil) + c.Assert(err, IsNil) + c.Assert(result.Name(), Equals, "hello-world") + c.Assert(result.Revision, Equals, snap.R(26)) + c.Assert(result.SnapID, Equals, helloWorldSnapID) +} + func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryLookupRefreshLocalSnap(c *C) { defer mockRFC(func(_ *Store, _ []*currentSnapJSON, _ *auth.UserState) ([]*snapDetails, error) { panic("unexpected call to refreshForCandidates") @@ -3082,6 +3243,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryListRefresh(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) // check device authorization is set, implicitly checking doRequest was used c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) @@ -3111,10 +3273,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) @@ -3125,7 +3286,6 @@ SnapID: helloWorldSnapID, Channel: "stable", Revision: snap.R(1), - Epoch: "0", }, }, nil) c.Assert(err, IsNil) @@ -3138,8 +3298,65 @@ c.Assert(results[0].Deltas, HasLen, 0) } +func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryListRefreshIgnoreValidation(c *C) { + mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) + // check device authorization is set, implicitly checking doRequest was used + c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) + + jsonReq, err := ioutil.ReadAll(r.Body) + c.Assert(err, IsNil) + var resp struct { + Snaps []map[string]interface{} `json:"snaps"` + Fields []string `json:"fields"` + } + + err = json.Unmarshal(jsonReq, &resp) + c.Assert(err, IsNil) + + c.Assert(resp.Snaps, HasLen, 1) + c.Assert(resp.Snaps[0], DeepEquals, map[string]interface{}{ + "snap_id": helloWorldSnapID, + "channel": "stable", + "revision": float64(1), + "epoch": "0", + "confinement": "", + "ignore_validation": true, + }) + c.Assert(resp.Fields, DeepEquals, detailFields) + + io.WriteString(w, MockUpdatesJSON) + })) + + c.Assert(mockServer, NotNil) + defer mockServer.Close() + + mockServerURL, _ := url.Parse(mockServer.URL) + cfg := Config{ + StoreBaseURL: mockServerURL, + } + authContext := &testAuthContext{c: c, device: t.device} + repo := New(&cfg, authContext) + c.Assert(repo, NotNil) + + results, err := repo.ListRefresh([]*RefreshCandidate{ + { + SnapID: helloWorldSnapID, + Channel: "stable", + Revision: snap.R(1), + IgnoreValidation: true, + }, + }, nil) + c.Assert(err, IsNil) + c.Assert(results, HasLen, 1) + c.Assert(results[0].Name(), Equals, "hello-world") + c.Assert(results[0].Revision, Equals, snap.R(26)) + c.Assert(results[0].SnapID, Equals, helloWorldSnapID) +} + func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryListRefreshDefaultChannelIsStable(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) // check device authorization is set, implicitly checking doRequest was used c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) @@ -3169,10 +3386,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) @@ -3182,7 +3398,6 @@ { SnapID: helloWorldSnapID, Revision: snap.R(1), - Epoch: "0", }, }, nil) c.Assert(err, IsNil) @@ -3199,6 +3414,7 @@ n := 0 var mockServer *httptest.Server mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) n++ if n < 4 { io.WriteString(w, "{") @@ -3218,10 +3434,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) @@ -3231,7 +3446,6 @@ SnapID: helloWorldSnapID, Channel: "stable", Revision: snap.R(1), - Epoch: "0", }}, nil) c.Assert(err, IsNil) c.Assert(n, Equals, 4) @@ -3244,10 +3458,12 @@ somewhatBrokenSrvCalls := 0 mockPermanentlyBrokenServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) permanentlyBrokenSrvCalls++ w.Header().Add("Content-Length", "1000") })) mockSomewhatBrokenServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) somewhatBrokenSrvCalls++ if somewhatBrokenSrvCalls > 3 { io.WriteString(w, MockUpdatesJSON) @@ -3260,20 +3476,18 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) c.Assert(repo, NotNil) - _, err = repo.refreshForCandidates([]*currentSnapJSON{{ + _, err := repo.refreshForCandidates([]*currentSnapJSON{{ SnapID: helloWorldSnapID, Channel: "stable", Revision: 1, - Epoch: "0", }}, nil) return err } @@ -3297,6 +3511,7 @@ n := 0 var mockServer *httptest.Server mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) n++ io.WriteString(w, "{") mockServer.CloseClientConnections() @@ -3306,29 +3521,28 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) c.Assert(repo, NotNil) - _, err = repo.refreshForCandidates([]*currentSnapJSON{{ + _, err := repo.refreshForCandidates([]*currentSnapJSON{{ SnapID: helloWorldSnapID, Channel: "stable", Revision: 1, - Epoch: "0", }}, nil) c.Assert(err, NotNil) - c.Assert(err, ErrorMatches, `^Post http://127.0.0.1:.*?/updates/: EOF$`) + c.Assert(err, ErrorMatches, `^Post http://127.0.0.1:.*?/metadata: EOF$`) c.Assert(n, Equals, 5) } func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryRefreshForCandidatesUnauthorised(c *C) { n := 0 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) n++ c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) w.WriteHeader(401) @@ -3338,31 +3552,29 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) c.Assert(repo, NotNil) - _, err = repo.refreshForCandidates([]*currentSnapJSON{{ + _, err := repo.refreshForCandidates([]*currentSnapJSON{{ SnapID: helloWorldSnapID, Channel: "stable", Revision: 24, - Epoch: "0", }}, nil) c.Assert(n, Equals, 1) - c.Assert(err, ErrorMatches, `cannot query the store for updates: got unexpected HTTP status code 401 via POST to "http://.*?/updates/"`) + c.Assert(err, ErrorMatches, `cannot query the store for updates: got unexpected HTTP status code 401 via POST to "http://.*?/metadata"`) } func (t *remoteRepoTestSuite) TestRefreshForCandidatesFailOnDNS(c *C) { - bulkURI, err := url.Parse("http://nonexistingserver909123.com/updates/") + baseURL, err := url.Parse("http://nonexistingserver909123.com/") c.Assert(err, IsNil) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: baseURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) @@ -3372,7 +3584,6 @@ SnapID: helloWorldSnapID, Channel: "stable", Revision: 24, - Epoch: "0", }}, nil) // the error differs depending on whether a proxy is in use (e.g. on travis), so don't inspect error message c.Assert(err, NotNil) @@ -3381,34 +3592,34 @@ func (t *remoteRepoTestSuite) TestRefreshForCandidates500(c *C) { n := 0 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) n++ w.WriteHeader(500) })) c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) c.Assert(repo, NotNil) - _, err = repo.refreshForCandidates([]*currentSnapJSON{{ + _, err := repo.refreshForCandidates([]*currentSnapJSON{{ SnapID: helloWorldSnapID, Channel: "stable", Revision: 24, - Epoch: "0", }}, nil) - c.Assert(err, ErrorMatches, `cannot query the store for updates: got unexpected HTTP status code 500 via POST to "http://.*?/updates/"`) + c.Assert(err, ErrorMatches, `cannot query the store for updates: got unexpected HTTP status code 500 via POST to "http://.*?/metadata"`) c.Assert(n, Equals, 5) } func (t *remoteRepoTestSuite) TestRefreshForCandidates500DurationExceeded(c *C) { n := 0 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) n++ time.Sleep(time.Duration(2) * time.Second) w.WriteHeader(500) @@ -3416,22 +3627,20 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) c.Assert(repo, NotNil) - _, err = repo.refreshForCandidates([]*currentSnapJSON{{ + _, err := repo.refreshForCandidates([]*currentSnapJSON{{ SnapID: helloWorldSnapID, Channel: "stable", Revision: 24, - Epoch: "0", }}, nil) - c.Assert(err, ErrorMatches, `cannot query the store for updates: got unexpected HTTP status code 500 via POST to "http://.*?/updates/"`) + c.Assert(err, ErrorMatches, `cannot query the store for updates: got unexpected HTTP status code 500 via POST to "http://.*?/metadata"`) c.Assert(n, Equals, 1) } @@ -3451,6 +3660,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryListRefreshSkipCurrent(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) jsonReq, err := ioutil.ReadAll(r.Body) c.Assert(err, IsNil) var resp struct { @@ -3475,10 +3685,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, nil) c.Assert(repo, NotNil) @@ -3487,7 +3696,6 @@ SnapID: helloWorldSnapID, Channel: "stable", Revision: snap.R(26), - Epoch: "0", }}, nil) c.Assert(err, IsNil) c.Assert(results, HasLen, 0) @@ -3495,6 +3703,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryListRefreshSkipBlocked(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) jsonReq, err := ioutil.ReadAll(r.Body) c.Assert(err, IsNil) @@ -3520,10 +3729,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, nil) c.Assert(repo, NotNil) @@ -3532,7 +3740,6 @@ SnapID: helloWorldSnapID, Channel: "stable", Revision: snap.R(25), - Epoch: "0", Block: []snap.Revision{snap.R(26)}, }}, nil) c.Assert(err, IsNil) @@ -3608,14 +3815,14 @@ defer restore() mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) c.Check(r.Header.Get("X-Ubuntu-Delta-Formats"), Equals, t.deltaFormatStr) })) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, nil) c.Assert(repo, NotNil) @@ -3624,7 +3831,6 @@ SnapID: helloWorldSnapID, Channel: "stable", Revision: 1, - Epoch: "0", }}, nil) } } @@ -3635,6 +3841,7 @@ c.Assert(os.Setenv("SNAPD_USE_DELTAS_EXPERIMENTAL", "1"), IsNil) mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) c.Check(r.Header.Get("X-Ubuntu-Delta-Formats"), Equals, `xdelta3`) jsonReq, err := ioutil.ReadAll(r.Body) c.Assert(err, IsNil) @@ -3662,10 +3869,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, nil) c.Assert(repo, NotNil) @@ -3674,7 +3880,6 @@ SnapID: helloWorldSnapID, Channel: "stable", Revision: snap.R(24), - Epoch: "0", }}, nil) c.Assert(err, IsNil) c.Assert(results, HasLen, 1) @@ -3706,6 +3911,7 @@ c.Assert(os.Setenv("SNAPD_USE_DELTAS_EXPERIMENTAL", "0"), IsNil) mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "POST", metadataPath) c.Check(r.Header.Get("X-Ubuntu-Delta-Formats"), Equals, ``) jsonReq, err := ioutil.ReadAll(r.Body) c.Assert(err, IsNil) @@ -3733,10 +3939,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, nil) c.Assert(repo, NotNil) @@ -3745,7 +3950,6 @@ SnapID: helloWorldSnapID, Channel: "stable", Revision: snap.R(24), - Epoch: "0", }}, nil) c.Assert(err, IsNil) c.Assert(results, HasLen, 1) @@ -3754,25 +3958,24 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryUpdateNotSendLocalRevs(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c.Error(r.URL.Path) c.Fatal("no network request expected") })) c.Assert(mockServer, NotNil) defer mockServer.Close() - bulkURI, err := url.Parse(mockServer.URL + "/updates/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - BulkURI: bulkURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, nil) c.Assert(repo, NotNil) - _, err = repo.ListRefresh([]*RefreshCandidate{{ + _, err := repo.ListRefresh([]*RefreshCandidate{{ SnapID: helloWorldSnapID, Channel: "stable", Revision: snap.R(-2), - Epoch: "0", }}, nil) c.Assert(err, IsNil) } @@ -3897,18 +4100,16 @@ } func (t *remoteRepoTestSuite) TestDefaultConfig(c *C) { - c.Check(strings.HasPrefix(defaultConfig.SearchURI.String(), "https://api.snapcraft.io/api/v1/snaps/search"), Equals, true) - c.Check(strings.HasPrefix(defaultConfig.BulkURI.String(), "https://api.snapcraft.io/api/v1/snaps/metadata"), Equals, true) - c.Check(defaultConfig.AssertionsURI.String(), Equals, "https://api.snapcraft.io/api/v1/snaps/assertions/") + c.Check(defaultConfig.StoreBaseURL.String(), Equals, "https://api.snapcraft.io/") + c.Check(defaultConfig.AssertionsBaseURL.String(), Equals, "https://api.snapcraft.io/api/v1/snaps") } func (t *remoteRepoTestSuite) TestNew(c *C) { aStore := New(nil, nil) - fields := strings.Join(detailFields, ",") // check for fields c.Check(aStore.detailFields, DeepEquals, detailFields) - c.Check(aStore.searchURI.Query().Get("fields"), Equals, fields) - c.Check(aStore.detailsURI.Query().Get("fields"), Equals, fields) + c.Check(aStore.searchURI.Query(), DeepEquals, url.Values{}) + c.Check(aStore.detailsURI.Query(), DeepEquals, url.Values{}) c.Check(aStore.bulkURI.Query(), DeepEquals, url.Values{}) c.Check(aStore.sectionsURI.Query(), DeepEquals, url.Values{}) c.Check(aStore.assertionsURI.Query(), DeepEquals, url.Values{}) @@ -3934,11 +4135,12 @@ restore := asserts.MockMaxSupportedFormat(asserts.SnapDeclarationType, 88) defer restore() mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", "/assertions/.*") // check device authorization is set, implicitly checking doRequest was used c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion") - c.Check(r.URL.Path, Equals, "/assertions/snap-declaration/16/snapidfoo") + c.Check(r.URL.Path, Matches, ".*/snap-declaration/16/snapidfoo") c.Check(r.URL.Query().Get("max-format"), Equals, "88") io.WriteString(w, testAssertion) })) @@ -3946,11 +4148,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - assertionsURI, err := url.Parse(mockServer.URL + "/assertions/") - c.Assert(err, IsNil) - + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - AssertionsURI: assertionsURI, + AssertionsBaseURL: mockServerURL, } authContext := &testAuthContext{c: c, device: t.device} repo := New(&cfg, authContext) @@ -3963,8 +4163,9 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryAssertionNotFound(c *C) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", "/assertions/.*") c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion") - c.Check(r.URL.Path, Equals, "/assertions/snap-declaration/16/snapidfoo") + c.Check(r.URL.Path, Matches, ".*/snap-declaration/16/snapidfoo") w.Header().Set("Content-Type", "application/problem+json") w.WriteHeader(404) io.WriteString(w, `{"status": 404,"title": "not found"}`) @@ -3973,18 +4174,19 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - assertionsURI, err := url.Parse(mockServer.URL + "/assertions/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - AssertionsURI: assertionsURI, + AssertionsBaseURL: mockServerURL, } repo := New(&cfg, nil) - _, err = repo.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, nil) - c.Check(err, DeepEquals, &AssertionNotFoundError{ - Ref: &asserts.Ref{ - Type: asserts.SnapDeclarationType, - PrimaryKey: []string{"16", "snapidfoo"}, + _, err := repo.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, nil) + c.Check(asserts.IsNotFound(err), Equals, true) + c.Check(err, DeepEquals, &asserts.NotFoundError{ + Type: asserts.SnapDeclarationType, + Headers: map[string]string{ + "series": "16", + "snap-id": "snapidfoo", }, }) } @@ -3992,6 +4194,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreRepositoryAssertion500(c *C) { var n = 0 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", "/assertions/.*") n++ w.WriteHeader(500) })) @@ -3999,14 +4202,13 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - assertionsURI, err := url.Parse(mockServer.URL + "/assertions/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - AssertionsURI: assertionsURI, + AssertionsBaseURL: mockServerURL, } repo := New(&cfg, nil) - _, err = repo.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, nil) + _, err := repo.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, nil) c.Assert(err, ErrorMatches, `cannot fetch assertion: got unexpected HTTP status code 500 via .+`) c.Assert(n, Equals, 5) } @@ -4015,6 +4217,7 @@ suggestedCurrency := "GBP" mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", detailsPathPattern) w.Header().Set("X-Suggested-Currency", suggestedCurrency) w.WriteHeader(200) @@ -4024,10 +4227,9 @@ c.Assert(mockServer, NotNil) defer mockServer.Close() - detailsURI, err := url.Parse(mockServer.URL + "/details/") - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockServer.URL) cfg := Config{ - DetailsURI: detailsURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, nil) c.Assert(repo, NotNil) @@ -4057,6 +4259,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreDecorateOrders(c *C) { mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", ordersPath) // check device authorization is set, implicitly checking doRequest was used c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) c.Check(r.Header.Get("Accept"), Equals, jsonContentType) @@ -4068,12 +4271,10 @@ c.Assert(mockPurchasesServer, NotNil) defer mockPurchasesServer.Close() - ordersURI, err := url.Parse(mockPurchasesServer.URL + ordersPath) - c.Assert(err, IsNil) - + mockServerURL, _ := url.Parse(mockPurchasesServer.URL) authContext := &testAuthContext{c: c, device: t.device, user: t.user} cfg := Config{ - OrdersURI: ordersURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, authContext) c.Assert(repo, NotNil) @@ -4095,7 +4296,7 @@ snaps := []*snap.Info{helloWorld, funkyApp, otherApp, otherApp2} - err = repo.decorateOrders(snaps, t.user) + err := repo.decorateOrders(snaps, t.user) c.Assert(err, IsNil) c.Check(helloWorld.MustBuy, Equals, false) @@ -4106,6 +4307,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreDecorateOrdersFailedAccess(c *C) { mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", ordersPath) c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user)) c.Check(r.Header.Get("Accept"), Equals, jsonContentType) c.Check(r.URL.Path, Equals, ordersPath) @@ -4116,10 +4318,9 @@ c.Assert(mockPurchasesServer, NotNil) defer mockPurchasesServer.Close() - ordersURI, err := url.Parse(mockPurchasesServer.URL + ordersPath) - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockPurchasesServer.URL) cfg := Config{ - OrdersURI: ordersURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, nil) c.Assert(repo, NotNil) @@ -4141,7 +4342,7 @@ snaps := []*snap.Info{helloWorld, funkyApp, otherApp, otherApp2} - err = repo.decorateOrders(snaps, t.user) + err := repo.decorateOrders(snaps, t.user) c.Assert(err, NotNil) c.Check(helloWorld.MustBuy, Equals, true) @@ -4185,6 +4386,7 @@ requestRecieved := false mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c.Error(r.URL.Path) c.Check(r.Header.Get("Accept"), Equals, jsonContentType) requestRecieved = true io.WriteString(w, `{"orders": []}`) @@ -4193,10 +4395,9 @@ c.Assert(mockPurchasesServer, NotNil) defer mockPurchasesServer.Close() - ordersURI, err := url.Parse(mockPurchasesServer.URL + ordersPath) - c.Assert(err, IsNil) + mockServerURL, _ := url.Parse(mockPurchasesServer.URL) cfg := Config{ - OrdersURI: ordersURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, nil) @@ -4213,15 +4414,11 @@ snaps := []*snap.Info{helloWorld, funkyApp} // There should be no request to the purchase server. - err = repo.decorateOrders(snaps, t.user) + err := repo.decorateOrders(snaps, t.user) c.Assert(err, IsNil) c.Check(requestRecieved, Equals, false) } -const ordersPath = "/api/v1/snaps/purchases/orders" -const buyPath = "/api/v1/snaps/purchases/buy" -const customersMePath = "/api/v1/snaps/purchases/customers/me" - func (t *remoteRepoTestSuite) TestUbuntuStoreDecorateOrdersSingle(c *C) { mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user)) @@ -4234,12 +4431,10 @@ c.Assert(mockPurchasesServer, NotNil) defer mockPurchasesServer.Close() - ordersURI, err := url.Parse(mockPurchasesServer.URL + ordersPath) - c.Assert(err, IsNil) - + mockServerURL, _ := url.Parse(mockPurchasesServer.URL) authContext := &testAuthContext{c: c, device: t.device, user: t.user} cfg := Config{ - OrdersURI: ordersURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, authContext) c.Assert(repo, NotNil) @@ -4250,7 +4445,7 @@ snaps := []*snap.Info{helloWorld} - err = repo.decorateOrders(snaps, t.user) + err := repo.decorateOrders(snaps, t.user) c.Assert(err, IsNil) c.Check(helloWorld.MustBuy, Equals, false) } @@ -4272,6 +4467,7 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreDecorateOrdersSingleNotFound(c *C) { mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", ordersPath) c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user)) c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) c.Check(r.Header.Get("Accept"), Equals, jsonContentType) @@ -4283,12 +4479,10 @@ c.Assert(mockPurchasesServer, NotNil) defer mockPurchasesServer.Close() - ordersURI, err := url.Parse(mockPurchasesServer.URL + ordersPath) - c.Assert(err, IsNil) - + mockServerURL, _ := url.Parse(mockPurchasesServer.URL) authContext := &testAuthContext{c: c, device: t.device, user: t.user} cfg := Config{ - OrdersURI: ordersURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, authContext) c.Assert(repo, NotNil) @@ -4299,7 +4493,7 @@ snaps := []*snap.Info{helloWorld} - err = repo.decorateOrders(snaps, t.user) + err := repo.decorateOrders(snaps, t.user) c.Assert(err, NotNil) c.Check(helloWorld.MustBuy, Equals, true) } @@ -4317,12 +4511,10 @@ c.Assert(mockPurchasesServer, NotNil) defer mockPurchasesServer.Close() - ordersURI, err := url.Parse(mockPurchasesServer.URL + ordersPath) - c.Assert(err, IsNil) - + mockServerURL, _ := url.Parse(mockPurchasesServer.URL) authContext := &testAuthContext{c: c, device: t.device, user: t.user} cfg := Config{ - OrdersURI: ordersURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, authContext) c.Assert(repo, NotNil) @@ -4333,7 +4525,7 @@ snaps := []*snap.Info{helloWorld} - err = repo.decorateOrders(snaps, t.user) + err := repo.decorateOrders(snaps, t.user) c.Assert(err, NotNil) c.Check(helloWorld.MustBuy, Equals, true) } @@ -4436,27 +4628,24 @@ func (t *remoteRepoTestSuite) TestUbuntuStoreBuy500(c *C) { n := 0 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - n++ - w.WriteHeader(500) + switch r.URL.Path { + case detailsPath("hello-world"): + n++ + w.WriteHeader(500) + case buyPath: + case customersMePath: + // default 200 response + default: + c.Fatalf("unexpected query %s %s", r.Method, r.URL.Path) + } })) c.Assert(mockServer, NotNil) defer mockServer.Close() - mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - })) - - detailsURI, err := url.Parse(mockServer.URL) - c.Assert(err, IsNil) - buyURI, err := url.Parse(mockPurchasesServer.URL + buyPath) - c.Assert(err, IsNil) - customersMeURI, err := url.Parse(mockPurchasesServer.URL + customersMePath) - c.Assert(err, IsNil) - + mockServerURL, _ := url.Parse(mockServer.URL) authContext := &testAuthContext{c: c, device: t.device, user: t.user} cfg := Config{ - CustomersMeURI: customersMeURI, - DetailsURI: detailsURI, - BuyURI: buyURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, authContext) c.Assert(repo, NotNil) @@ -4466,44 +4655,33 @@ Currency: "USD", Price: 1, } - _, err = repo.Buy(buyOptions, t.user) + _, err := repo.Buy(buyOptions, t.user) c.Assert(err, NotNil) } func (t *remoteRepoTestSuite) TestUbuntuStoreBuy(c *C) { for _, test := range buyTests { searchServerCalled := false - mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - c.Check(r.Method, Equals, "GET") - c.Check(r.URL.Path, Equals, "/hello-world") - w.Header().Set("Content-Type", "application/hal+json") - w.Header().Set("X-Suggested-Currency", test.suggestedCurrency) - w.WriteHeader(200) - io.WriteString(w, MockDetailsJSON) - searchServerCalled = true - })) - c.Assert(mockServer, NotNil) - defer mockServer.Close() - purchaseServerGetCalled := false purchaseServerPostCalled := false - mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case "GET": - // check device authorization is set, implicitly checking doRequest was used + mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case detailsPath("hello-world"): + c.Assert(r.Method, Equals, "GET") + w.Header().Set("Content-Type", "application/hal+json") + w.Header().Set("X-Suggested-Currency", test.suggestedCurrency) + w.WriteHeader(200) + io.WriteString(w, MockDetailsJSON) + searchServerCalled = true + case ordersPath: + c.Assert(r.Method, Equals, "GET") c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) c.Check(r.Header.Get("Accept"), Equals, jsonContentType) c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user)) - switch r.URL.Path { - case ordersPath: - io.WriteString(w, `{"orders": []}`) - case customersMePath: - io.WriteString(w, customersMeValid) - default: - c.Fail() - } + io.WriteString(w, `{"orders": []}`) purchaseServerGetCalled = true - case "POST": + case buyPath: + c.Assert(r.Method, Equals, "POST") // check device authorization is set, implicitly checking doRequest was used c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) c.Check(r.Header.Get("Authorization"), Equals, t.expectedAuthorization(c, t.user)) @@ -4517,6 +4695,7 @@ io.WriteString(w, test.buyResponse) } else { w.WriteHeader(test.buyStatus) + // TODO(matt): this is fugly! fmt.Fprintf(w, ` { "error_list": [ @@ -4530,25 +4709,16 @@ purchaseServerPostCalled = true default: - c.Error("Unexpected request method: ", r.Method) + c.Fatalf("unexpected query %s %s", r.Method, r.URL.Path) } })) + c.Assert(mockServer, NotNil) + defer mockServer.Close() - c.Assert(mockPurchasesServer, NotNil) - defer mockPurchasesServer.Close() - - detailsURI, err := url.Parse(mockServer.URL) - c.Assert(err, IsNil) - ordersURI, err := url.Parse(mockPurchasesServer.URL + ordersPath) - c.Assert(err, IsNil) - buyURI, err := url.Parse(mockPurchasesServer.URL + buyPath) - c.Assert(err, IsNil) - + mockServerURL, _ := url.Parse(mockServer.URL) authContext := &testAuthContext{c: c, device: t.device, user: t.user} cfg := Config{ - DetailsURI: detailsURI, - OrdersURI: ordersURI, - BuyURI: buyURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, authContext) c.Assert(repo, NotNil) @@ -4753,6 +4923,7 @@ for _, test := range readyToBuyTests { purchaseServerGetCalled := 0 mockPurchasesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(c, r, "GET", customersMePath) switch r.Method { case "GET": // check device authorization is set, implicitly checking doRequest was used @@ -4770,17 +4941,15 @@ c.Assert(mockPurchasesServer, NotNil) defer mockPurchasesServer.Close() - customersMeURI, err := url.Parse(mockPurchasesServer.URL + customersMePath) - c.Assert(err, IsNil) - + mockServerURL, _ := url.Parse(mockPurchasesServer.URL) authContext := &testAuthContext{c: c, device: t.device, user: t.user} cfg := Config{ - CustomersMeURI: customersMeURI, + StoreBaseURL: mockServerURL, } repo := New(&cfg, authContext) c.Assert(repo, NotNil) - err = repo.ReadyToBuy(t.user) + err := repo.ReadyToBuy(t.user) test.Test(c, err) c.Check(purchaseServerGetCalled, Equals, test.NumOfCalls) } @@ -4819,3 +4988,68 @@ _, err = sto.doRequest(context.TODO(), sto.client, reqOptions, t.user) c.Assert(err, IsNil) } + +type cacheObserver struct { + inCache map[string]bool + + gets []string + puts []string +} + +func (co *cacheObserver) Get(cacheKey, targetPath string) error { + co.gets = append(co.gets, fmt.Sprintf("%s:%s", cacheKey, targetPath)) + if !co.inCache[cacheKey] { + return fmt.Errorf("cannot find %s in cache", cacheKey) + } + return nil +} +func (co *cacheObserver) Put(cacheKey, sourcePath string) error { + co.puts = append(co.puts, fmt.Sprintf("%s:%s", cacheKey, sourcePath)) + return nil +} + +func (t *remoteRepoTestSuite) TestDownloadCacheHit(c *C) { + oldCache := t.store.cacher + defer func() { t.store.cacher = oldCache }() + obs := &cacheObserver{inCache: map[string]bool{"the-snaps-sha3_384": true}} + t.store.cacher = obs + + download = func(ctx context.Context, name, sha3, url string, user *auth.UserState, s *Store, w io.ReadWriteSeeker, resume int64, pbar progress.Meter) error { + c.Fatalf("download should not be called when results come from the cache") + return nil + } + + snap := &snap.Info{} + snap.Sha3_384 = "the-snaps-sha3_384" + + path := filepath.Join(c.MkDir(), "downloaded-file") + err := t.store.Download(context.TODO(), "foo", path, &snap.DownloadInfo, nil, nil) + c.Assert(err, IsNil) + + c.Check(obs.gets, DeepEquals, []string{fmt.Sprintf("%s:%s", snap.Sha3_384, path)}) + c.Check(obs.puts, IsNil) +} + +func (t *remoteRepoTestSuite) TestDownloadCacheMiss(c *C) { + oldCache := t.store.cacher + defer func() { t.store.cacher = oldCache }() + obs := &cacheObserver{inCache: map[string]bool{}} + t.store.cacher = obs + + downloadWasCalled := false + download = func(ctx context.Context, name, sha3, url string, user *auth.UserState, s *Store, w io.ReadWriteSeeker, resume int64, pbar progress.Meter) error { + downloadWasCalled = true + return nil + } + + snap := &snap.Info{} + snap.Sha3_384 = "the-snaps-sha3_384" + + path := filepath.Join(c.MkDir(), "downloaded-file") + err := t.store.Download(context.TODO(), "foo", path, &snap.DownloadInfo, nil, nil) + c.Assert(err, IsNil) + c.Check(downloadWasCalled, Equals, true) + + c.Check(obs.gets, DeepEquals, []string{fmt.Sprintf("the-snaps-sha3_384:%s", path)}) + c.Check(obs.puts, DeepEquals, []string{fmt.Sprintf("the-snaps-sha3_384:%s", path)}) +} diff -Nru snapd-2.28.5/store/userinfo_test.go snapd-2.29.3/store/userinfo_test.go --- snapd-2.28.5/store/userinfo_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/store/userinfo_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -20,7 +20,6 @@ package store import ( - "bytes" "fmt" "net/http" "net/http/httptest" @@ -36,6 +35,8 @@ type userInfoSuite struct { testutil.BaseTest + + restoreLogger func() } var _ = check.Suite(&userInfoSuite{}) @@ -53,9 +54,7 @@ }` func (t *userInfoSuite) SetUpTest(c *check.C) { - l, err := logger.New(&bytes.Buffer{}, logger.DefaultFlags) - c.Assert(err, check.IsNil) - logger.SetLogger(l) + _, t.restoreLogger = logger.MockLogger() MockDefaultRetryStrategy(&t.BaseTest, retry.LimitCount(6, retry.LimitTime(1*time.Second, retry.Exponential{ @@ -65,6 +64,10 @@ ))) } +func (t *userInfoSuite) TearDownTest(c *check.C) { + t.restoreLogger() +} + func (s *userInfoSuite) redirectToTestSSO(handler func(http.ResponseWriter, *http.Request)) { server := httptest.NewServer(http.HandlerFunc(handler)) s.BaseTest.AddCleanup(func() { server.Close() }) diff -Nru snapd-2.28.5/systemd/export_test.go snapd-2.29.3/systemd/export_test.go --- snapd-2.28.5/systemd/export_test.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/systemd/export_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -25,8 +25,7 @@ ) var ( - SystemdRun = run // NOTE: plain Run clashes with check.v1 - Jctl = jctl + Jctl = jctl ) func MockStopDelays(checkDelay, notifyDelay time.Duration) func() { diff -Nru snapd-2.28.5/systemd/systemd.go snapd-2.29.3/systemd/systemd.go --- snapd-2.28.5/systemd/systemd.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/systemd/systemd.go 2017-10-27 06:23:41.000000000 +0000 @@ -45,8 +45,8 @@ stopNotifyDelay = 20 * time.Second ) -// run calls systemctl with the given args, returning its standard output (and wrapped error) -func run(args ...string) ([]byte, error) { +// systemctlCmd calls systemctl with the given args, returning its standard output (and wrapped error) +var systemctlCmd = func(args ...string) ([]byte, error) { bs, err := exec.Command("systemctl", args...).CombinedOutput() if err != nil { exitCode, _ := osutil.ExitCode(err) @@ -56,14 +56,25 @@ return bs, nil } -// SystemctlCmd is called from the commands to actually call out to +// MockSystemctl is called from the commands to actually call out to // systemctl. It's exported so it can be overridden by testing. -var SystemctlCmd = run +func MockSystemctl(f func(args ...string) ([]byte, error)) func() { + oldSystemctlCmd := systemctlCmd + systemctlCmd = f + return func() { + systemctlCmd = oldSystemctlCmd + } +} + +func Available() error { + _, err := systemctlCmd("--version") + return err +} var osutilStreamCommand = osutil.StreamCommand // jctl calls journalctl to get the JSON logs of the given services. -func jctl(svcs []string, n string, follow bool) (io.ReadCloser, error) { +var jctl = func(svcs []string, n string, follow bool) (io.ReadCloser, error) { // args will need two entries per service, plus a fixed number (give or take // one) for the initial options. args := make([]string, 0, 2*len(svcs)+6) @@ -79,8 +90,13 @@ return osutilStreamCommand("journalctl", args...) } -// JournalctlCmd is called from Logs to run journalctl; exported for testing. -var JournalctlCmd = jctl +func MockJournalctl(f func(svcs []string, n string, follow bool) (io.ReadCloser, error)) func() { + oldJctl := jctl + jctl = f + return func() { + jctl = oldJctl + } +} // Systemd exposes a minimal interface to manage systemd via the systemctl command. type Systemd interface { @@ -126,31 +142,31 @@ // DaemonReload reloads systemd's configuration. func (*systemd) DaemonReload() error { - _, err := SystemctlCmd("daemon-reload") + _, err := systemctlCmd("daemon-reload") return err } // Enable the given service func (s *systemd) Enable(serviceName string) error { - _, err := SystemctlCmd("--root", s.rootDir, "enable", serviceName) + _, err := systemctlCmd("--root", s.rootDir, "enable", serviceName) return err } // Disable the given service func (s *systemd) Disable(serviceName string) error { - _, err := SystemctlCmd("--root", s.rootDir, "disable", serviceName) + _, err := systemctlCmd("--root", s.rootDir, "disable", serviceName) return err } // Start the given service func (*systemd) Start(serviceName string) error { - _, err := SystemctlCmd("start", serviceName) + _, err := systemctlCmd("start", serviceName) return err } // LogReader for the given services func (*systemd) LogReader(serviceNames []string, n string, follow bool) (io.ReadCloser, error) { - return JournalctlCmd(serviceNames, n, follow) + return jctl(serviceNames, n, follow) } var statusregex = regexp.MustCompile(`(?m)^(?:(.+?)=(.*)|(.*))?$`) @@ -168,7 +184,7 @@ cmd[0] = "show" cmd[1] = "--property=" + strings.Join(expected, ",") copy(cmd[2:], serviceNames) - bs, err := SystemctlCmd(cmd...) + bs, err := systemctlCmd(cmd...) if err != nil { return nil, err } @@ -242,7 +258,7 @@ // Stop the given service, and wait until it has stopped. func (s *systemd) Stop(serviceName string, timeout time.Duration) error { - if _, err := SystemctlCmd("stop", serviceName); err != nil { + if _, err := systemctlCmd("stop", serviceName); err != nil { return err } @@ -260,7 +276,7 @@ case <-giveup.C: break loop case <-check.C: - bs, err := SystemctlCmd("show", "--property=ActiveState", serviceName) + bs, err := systemctlCmd("show", "--property=ActiveState", serviceName) if err != nil { return err } @@ -282,7 +298,7 @@ // Kill all processes of the unit with the given signal func (s *systemd) Kill(serviceName, signal string) error { - _, err := SystemctlCmd("kill", serviceName, "-s", signal) + _, err := systemctlCmd("kill", serviceName, "-s", signal) return err } @@ -412,6 +428,7 @@ c := fmt.Sprintf(`[Unit] Description=Mount unit for %s +Before=snapd.service [Mount] What=%s diff -Nru snapd-2.28.5/systemd/systemd_test.go snapd-2.29.3/systemd/systemd_test.go --- snapd-2.28.5/systemd/systemd_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/systemd/systemd_test.go 2017-10-27 06:23:41.000000000 +0000 @@ -64,6 +64,9 @@ jfollows []bool rep *testreporter + + restoreSystemctl func() + restoreJournalctl func() } var _ = Suite(&SystemdTestSuite{}) @@ -76,13 +79,13 @@ // force UTC timezone, for reproducible timestamps os.Setenv("TZ", "") - SystemctlCmd = s.myRun + s.restoreSystemctl = MockSystemctl(s.myRun) s.i = 0 s.argses = nil s.errors = nil s.outs = nil - JournalctlCmd = s.myJctl + s.restoreJournalctl = MockJournalctl(s.myJctl) s.j = 0 s.jns = nil s.jsvcs = nil @@ -94,8 +97,8 @@ } func (s *SystemdTestSuite) TearDownTest(c *C) { - SystemctlCmd = SystemdRun - JournalctlCmd = Jctl + s.restoreSystemctl() + s.restoreJournalctl() } func (s *SystemdTestSuite) myRun(args ...string) (out []byte, err error) { @@ -336,6 +339,12 @@ c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "disable", "foo"}}) } +func (s *SystemdTestSuite) TestAvailable(c *C) { + err := Available() + c.Assert(err, IsNil) + c.Check(s.argses, DeepEquals, [][]string{{"--version"}}) +} + func (s *SystemdTestSuite) TestEnable(c *C) { err := New("xyzzy", s.rep).Enable("foo") c.Assert(err, IsNil) @@ -444,6 +453,7 @@ c.Assert(err, IsNil) c.Assert(string(mount), Equals, fmt.Sprintf(`[Unit] Description=Mount unit for foo +Before=snapd.service [Mount] What=%s @@ -467,6 +477,7 @@ c.Assert(err, IsNil) c.Assert(string(mount), Equals, fmt.Sprintf(`[Unit] Description=Mount unit for foodir +Before=snapd.service [Mount] What=%s @@ -509,6 +520,7 @@ c.Assert(err, IsNil) c.Assert(string(mount), Equals, fmt.Sprintf(`[Unit] Description=Mount unit for foo +Before=snapd.service [Mount] What=%s @@ -547,6 +559,7 @@ c.Assert(err, IsNil) c.Assert(string(mount), Equals, fmt.Sprintf(`[Unit] Description=Mount unit for foo +Before=snapd.service [Mount] What=%s diff -Nru snapd-2.28.5/tests/lib/dbus.sh snapd-2.29.3/tests/lib/dbus.sh --- snapd-2.28.5/tests/lib/dbus.sh 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/lib/dbus.sh 2017-10-23 06:17:27.000000000 +0000 @@ -4,6 +4,7 @@ local executable="$1" dbus-launch > dbus.env + # shellcheck disable=SC2046 export $(cat dbus.env) if [[ "$SPREAD_SYSTEM" == ubuntu-14.04-* ]]; then cat < /etc/init/dbus-provider.conf @@ -17,9 +18,9 @@ start dbus-provider else systemd-run --unit dbus-provider \ - --setenv=DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS \ - --setenv=DBUS_SESSION_BUS_PID=$DBUS_SESSION_BUS_PID \ - $executable + --setenv=DBUS_SESSION_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" \ + --setenv=DBUS_SESSION_BUS_PID="$DBUS_SESSION_BUS_PID" \ + "$executable" fi } diff -Nru snapd-2.28.5/tests/lib/fakestore/refresh/refresh.go snapd-2.29.3/tests/lib/fakestore/refresh/refresh.go --- snapd-2.28.5/tests/lib/fakestore/refresh/refresh.go 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/tests/lib/fakestore/refresh/refresh.go 2017-10-23 06:17:27.000000000 +0000 @@ -36,7 +36,6 @@ "github.com/snapcore/snapd/client" "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/snap" - "github.com/snapcore/snapd/store" ) func MakeFakeRefreshForSnaps(snaps []string, blobDir string) error { @@ -67,7 +66,7 @@ case 1: return as[0], nil case 0: - return nil, &store.AssertionNotFoundError{Ref: ref} + return nil, &asserts.NotFoundError{Type: ref.Type, Headers: headers} default: panic(fmt.Sprintf("multiple assertions when retrieving by primary key: %v", ref)) } @@ -192,7 +191,7 @@ func buildSnap(snapDir, targetDir string) (*info, error) { // build in /var/tmp (which is not a tempfs) - cmd := exec.Command("snapbuild", snapDir, targetDir) + cmd := exec.Command("snap", "pack", snapDir, targetDir) cmd.Env = append(os.Environ(), "TMPDIR=/var/tmp") output, err := cmd.CombinedOutput() if err != nil { diff -Nru snapd-2.28.5/tests/lib/fakestore/store/store.go snapd-2.29.3/tests/lib/fakestore/store/store.go --- snapd-2.28.5/tests/lib/fakestore/store/store.go 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/lib/fakestore/store/store.go 2017-10-23 06:17:27.000000000 +0000 @@ -193,7 +193,7 @@ } snapRev, devAcct, err := findSnapRevision(snapDigest, bs) - if err != nil && err != asserts.ErrNotFound { + if err != nil && !asserts.IsNotFound(err) { http.Error(w, fmt.Sprintf("can get info for: %v: %v", fn, err), 400) return nil, errInfo } @@ -476,19 +476,9 @@ return bs, nil } -func isAssertNotFound(err error) bool { - if err == asserts.ErrNotFound { - return true - } - if _, ok := err.(*store.AssertionNotFoundError); ok { - return true - } - return false -} - func (s *Store) retrieveAssertion(bs asserts.Backstore, assertType *asserts.AssertionType, primaryKey []string) (asserts.Assertion, error) { a, err := bs.Get(assertType, primaryKey, assertType.MaxSupportedFormat()) - if err == asserts.ErrNotFound && s.assertFallback { + if asserts.IsNotFound(err) && s.assertFallback { return s.fallback.Assertion(assertType, primaryKey, nil) } return a, err @@ -523,7 +513,7 @@ } a, err := s.retrieveAssertion(bs, typ, comps[1:]) - if isAssertNotFound(err) { + if asserts.IsNotFound(err) { w.Header().Set("Content-Type", "application/problem+json") w.WriteHeader(404) w.Write([]byte(`{"status": 404}`)) diff -Nru snapd-2.28.5/tests/lib/pkgdb.sh snapd-2.29.3/tests/lib/pkgdb.sh --- snapd-2.28.5/tests/lib/pkgdb.sh 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/lib/pkgdb.sh 2017-10-23 06:17:27.000000000 +0000 @@ -148,7 +148,7 @@ # will fail to install because the poor apt resolver does not get it case "$SPREAD_SYSTEM" in ubuntu-*|debian-*) - if [[ "$@" =~ "libudev-dev" ]]; then + if [[ "$*" =~ "libudev-dev" ]]; then apt-get install -y --only-upgrade systemd fi ;; diff -Nru snapd-2.28.5/tests/lib/prepare-project.sh snapd-2.29.3/tests/lib/prepare-project.sh --- snapd-2.28.5/tests/lib/prepare-project.sh 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/lib/prepare-project.sh 2017-10-23 06:17:27.000000000 +0000 @@ -155,13 +155,6 @@ . "$TESTSLIB/dirs.sh" if [ "$SPREAD_BACKEND" = external ]; then - # build test binaries - if [ ! -f "$GOHOME/bin/snapbuild" ]; then - mkdir -p "$GOHOME/bin" - snap install --edge test-snapd-snapbuild - cp "$SNAP_MOUNT_DIR/test-snapd-snapbuild/current/bin/snapbuild" "$GOHOME/bin/snapbuild" - snap remove test-snapd-snapbuild - fi # stop and disable autorefresh if [ -e "$SNAP_MOUNT_DIR/core/current/meta/hooks/configure" ]; then systemctl disable --now snapd.refresh.timer @@ -255,10 +248,7 @@ install_dependencies_from_published "$SNAPD_PUBLISHED_VERSION" fi -# Build snapbuild. -go get ./tests/lib/snapbuild # Build fakestore. - fakestore_tags= if [ "$REMOTE_STORE" = staging ]; then fakestore_tags="-tags withstagingkeys" diff -Nru snapd-2.28.5/tests/lib/prepare.sh snapd-2.29.3/tests/lib/prepare.sh --- snapd-2.28.5/tests/lib/prepare.sh 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/lib/prepare.sh 2017-10-23 06:17:27.000000000 +0000 @@ -56,6 +56,13 @@ ;; esac + case "$SPREAD_SYSTEM" in + ubuntu-*) + # also load snap-confine's apparmor profile + apparmor_parser -r squashfs-root/etc/apparmor.d/usr.lib.snapd.snap-confine.real + ;; + esac + # repack, cheating to speed things up (4sec vs 1.5min) mv "$snap" "${snap}.orig" mksnap_fast "squashfs-root" "$snap" @@ -73,7 +80,7 @@ } # Make sure we're running with the correct copied bits - for p in "$LIBEXECDIR/snapd/snap-exec" "$LIBEXECDIR/snapd/snap-confine" "$LIBEXECDIR/snapd/snap-discard-ns" "$LIBEXECDIR/snapd/snapd"; do + for p in "$LIBEXECDIR/snapd/snap-exec" "$LIBEXECDIR/snapd/snap-confine" "$LIBEXECDIR/snapd/snap-discard-ns" "$LIBEXECDIR/snapd/snapd" "$LIBEXECDIR/snapd/snap-update-ns"; do check_file "$p" "$core/usr/lib/snapd/$(basename "$p")" done for p in /usr/bin/snapctl /usr/bin/snap; do @@ -152,6 +159,10 @@ if [ "$REMOTE_STORE" = staging ]; then # shellcheck source=tests/lib/store.sh . "$TESTSLIB/store.sh" + # reset seeding data that is likely tainted with production keys + systemctl stop snapd.service snapd.socket + rm -rf /var/lib/snapd/assertions/* + rm -f /var/lib/snapd/state.json setup_staging_store fi @@ -327,7 +338,7 @@ EOF # build new core snap for the image - snapbuild "$UNPACKD" "$IMAGE_HOME" + snap pack "$UNPACKD" "$IMAGE_HOME" # FIXME: fetch directly once its in the assertion service cp "$TESTSLIB/assertions/pc-${REMOTE_STORE}.model" "$IMAGE_HOME/pc.model" diff -Nru snapd-2.28.5/tests/lib/snapbuild/main.go snapd-2.29.3/tests/lib/snapbuild/main.go --- snapd-2.28.5/tests/lib/snapbuild/main.go 2016-06-27 15:33:55.000000000 +0000 +++ snapd-2.29.3/tests/lib/snapbuild/main.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -// -*- 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 . - * - */ - -// snapbuild is a minimal executable wrapper around snap building to use for integration tests that need to build snaps under sudo. -package main - -import ( - "fmt" - "os" - - "github.com/snapcore/snapd/snap/snaptest" -) - -func main() { - if len(os.Args) != 3 { - fmt.Fprintf(os.Stderr, "snapbuild: expected sourceDir and targetDir\n") - os.Exit(1) - } - - snapPath, err := snaptest.BuildSquashfsSnap(os.Args[1], os.Args[2]) - if err != nil { - fmt.Fprintf(os.Stderr, "snapbuild: %v\n", err) - os.Exit(1) - } - fmt.Fprintf(os.Stdout, "built: %s\n", snapPath) -} diff -Nru snapd-2.28.5/tests/lib/snaps/basic-hooks/meta/hooks/configure snapd-2.29.3/tests/lib/snaps/basic-hooks/meta/hooks/configure --- snapd-2.28.5/tests/lib/snaps/basic-hooks/meta/hooks/configure 2017-09-20 07:05:01.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/basic-hooks/meta/hooks/configure 2017-10-23 06:17:27.000000000 +0000 @@ -3,5 +3,5 @@ echo "configure hook" command=$(snapctl get command) if [ "$command" = "dump-env" ]; then - env > $SNAP_DATA/hooks-env + env > "$SNAP_DATA"/hooks-env fi diff -Nru snapd-2.28.5/tests/lib/snaps/basic-run/bin/echo snapd-2.29.3/tests/lib/snaps/basic-run/bin/echo --- snapd-2.28.5/tests/lib/snaps/basic-run/bin/echo 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/basic-run/bin/echo 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "$@" diff -Nru snapd-2.28.5/tests/lib/snaps/basic-run/meta/snap.yaml snapd-2.29.3/tests/lib/snaps/basic-run/meta/snap.yaml --- snapd-2.28.5/tests/lib/snaps/basic-run/meta/snap.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/basic-run/meta/snap.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,6 @@ +name: basic-run +version: 1.0 +apps: + echo-data: + command: bin/echo $SNAP_DATA + diff -Nru snapd-2.28.5/tests/lib/snaps/snapctl-hooks/meta/hooks/configure snapd-2.29.3/tests/lib/snaps/snapctl-hooks/meta/hooks/configure --- snapd-2.28.5/tests/lib/snaps/snapctl-hooks/meta/hooks/configure 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/snapctl-hooks/meta/hooks/configure 2017-10-23 06:17:27.000000000 +0000 @@ -16,6 +16,14 @@ fi } +test_snapctl_set_bar_doc() { + echo "Setting bar document" + if ! snapctl set bar="{\"a\":{\"aa\":1,\"ab\":2},\"b\":3}"; then + echo "snapctl set unexpectedly failed" + exit 1 + fi +} + test_snapctl_get_foo() { echo "Getting foo" if ! output="$(snapctl get foo)"; then @@ -78,6 +86,9 @@ "test-nonexisting") test_nonexisting ;; + "test-snapctl-set-bar-doc") + test_snapctl_set_bar_doc + ;; "test-snapctl-set-foo") test_snapctl_set_foo ;; @@ -85,8 +96,8 @@ test_snapctl_get_foo ;; "test-snapctl-get-foo-null") - test_snapctl_get_foo_null - ;; + test_snapctl_get_foo_null + ;; "test-exit-one") test_exit_one ;; diff -Nru snapd-2.28.5/tests/lib/snaps/test-classic-cgroup/bin/read-fb snapd-2.29.3/tests/lib/snaps/test-classic-cgroup/bin/read-fb --- snapd-2.28.5/tests/lib/snaps/test-classic-cgroup/bin/read-fb 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-classic-cgroup/bin/read-fb 2017-11-08 06:21:40.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +cat /dev/fb0 diff -Nru snapd-2.28.5/tests/lib/snaps/test-classic-cgroup/bin/read-kmsg snapd-2.29.3/tests/lib/snaps/test-classic-cgroup/bin/read-kmsg --- snapd-2.28.5/tests/lib/snaps/test-classic-cgroup/bin/read-kmsg 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-classic-cgroup/bin/read-kmsg 2017-11-02 19:49:21.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +head -1 /dev/kmsg diff -Nru snapd-2.28.5/tests/lib/snaps/test-classic-cgroup/meta/snap.yaml snapd-2.29.3/tests/lib/snaps/test-classic-cgroup/meta/snap.yaml --- snapd-2.28.5/tests/lib/snaps/test-classic-cgroup/meta/snap.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-classic-cgroup/meta/snap.yaml 2017-11-02 19:49:21.000000000 +0000 @@ -0,0 +1,12 @@ +name: test-classic-cgroup +version: 1.0 +summary: Basic classic cgroup tester +description: A basic snap for testing classic and cgroup + (framebuffer) +confinement: classic + +apps: + read-fb: + command: bin/read-fb + read-kmsg: + command: bin/read-kmsg diff -Nru snapd-2.28.5/tests/lib/snaps/test-devmode-cgroup/bin/read-fb snapd-2.29.3/tests/lib/snaps/test-devmode-cgroup/bin/read-fb --- snapd-2.28.5/tests/lib/snaps/test-devmode-cgroup/bin/read-fb 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-devmode-cgroup/bin/read-fb 2017-11-08 06:21:40.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +cat /dev/fb0 diff -Nru snapd-2.28.5/tests/lib/snaps/test-devmode-cgroup/bin/read-kmsg snapd-2.29.3/tests/lib/snaps/test-devmode-cgroup/bin/read-kmsg --- snapd-2.28.5/tests/lib/snaps/test-devmode-cgroup/bin/read-kmsg 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-devmode-cgroup/bin/read-kmsg 2017-11-02 19:49:21.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +head -1 /dev/kmsg diff -Nru snapd-2.28.5/tests/lib/snaps/test-devmode-cgroup/meta/snap.yaml snapd-2.29.3/tests/lib/snaps/test-devmode-cgroup/meta/snap.yaml --- snapd-2.28.5/tests/lib/snaps/test-devmode-cgroup/meta/snap.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-devmode-cgroup/meta/snap.yaml 2017-11-02 19:49:21.000000000 +0000 @@ -0,0 +1,14 @@ +name: test-devmode-cgroup +version: 1.0 +summary: Basic devmode cgroup tester +description: A basic snap declaring a plug to a udev tagged interface + (framebuffer) +confinement: devmode + +apps: + read-fb: + command: bin/read-fb + plugs: [framebuffer] + read-kmsg: + command: bin/read-kmsg + plugs: [framebuffer] diff -Nru snapd-2.28.5/tests/lib/snaps/test-snapd-devpts/bin/openpty snapd-2.29.3/tests/lib/snaps/test-snapd-devpts/bin/openpty --- snapd-2.28.5/tests/lib/snaps/test-snapd-devpts/bin/openpty 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-snapd-devpts/bin/openpty 2017-11-09 09:07:17.000000000 +0000 @@ -0,0 +1,18 @@ +#!/usr/bin/python3 +import os +import pty +import sys + +files = os.listdir('/dev/pts') +if 'ptmx' not in files or len(files) != 1: + print("%s != ['ptmx']" % files) + sys.exit(1) + +pty.openpty() +files = os.listdir('/dev/pts') +if 'ptmx' not in files or '0' not in files or len(files) != 2: + print("%s != ['0', 'ptmx']" % files) + sys.exit(1) +print("PASS: %s" % files) + +sys.exit(0) diff -Nru snapd-2.28.5/tests/lib/snaps/test-snapd-devpts/bin/useptmx snapd-2.29.3/tests/lib/snaps/test-snapd-devpts/bin/useptmx --- snapd-2.28.5/tests/lib/snaps/test-snapd-devpts/bin/useptmx 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-snapd-devpts/bin/useptmx 2017-11-09 09:07:17.000000000 +0000 @@ -0,0 +1,20 @@ +#!/usr/bin/python3 +import os +import sys +from ctypes import * + +files = os.listdir('/dev/pts') +if 'ptmx' not in files or len(files) != 1: + print("%s != ['ptmx']" % files) + sys.exit(1) + +libc = CDLL("libc.so.6") +master = os.open("/dev/ptmx", os.O_RDWR | os.O_NOCTTY) +slavedev = os.ttyname(master) +libc.grantpt(master) +libc.unlockpt(master) +slave = os.open(slavedev, os.O_RDWR | os.O_NOCTTY) +files = os.listdir('/dev/pts') +print("PASS: %s" % files) + +sys.exit(0) diff -Nru snapd-2.28.5/tests/lib/snaps/test-snapd-devpts/meta/snap.yaml snapd-2.29.3/tests/lib/snaps/test-snapd-devpts/meta/snap.yaml --- snapd-2.28.5/tests/lib/snaps/test-snapd-devpts/meta/snap.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-snapd-devpts/meta/snap.yaml 2017-11-09 09:07:17.000000000 +0000 @@ -0,0 +1,9 @@ +name: test-snapd-devpts +version: 1.0 +apps: + openpty: + command: bin/openpty + plugs: [ physical-memory-observe ] + useptmx: + command: bin/useptmx + plugs: [ physical-memory-observe ] diff -Nru snapd-2.28.5/tests/lib/snaps/test-snapd-service/bin/start snapd-2.29.3/tests/lib/snaps/test-snapd-service/bin/start --- snapd-2.28.5/tests/lib/snaps/test-snapd-service/bin/start 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-snapd-service/bin/start 2017-10-27 12:23:38.000000000 +0000 @@ -1,5 +1,5 @@ #!/bin/sh - +snapctl get service-option > $SNAP_DATA/service-option while true; do echo "running" sleep 10 diff -Nru snapd-2.28.5/tests/lib/snaps/test-snapd-service/bin/start-other snapd-2.29.3/tests/lib/snaps/test-snapd-service/bin/start-other --- snapd-2.28.5/tests/lib/snaps/test-snapd-service/bin/start-other 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-snapd-service/bin/start-other 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,6 @@ +#!/bin/sh +snapctl get service-option > $SNAP_DATA/service-option +while true; do + echo "running" + sleep 10 +done diff -Nru snapd-2.28.5/tests/lib/snaps/test-snapd-service/meta/hooks/configure snapd-2.29.3/tests/lib/snaps/test-snapd-service/meta/hooks/configure --- snapd-2.28.5/tests/lib/snaps/test-snapd-service/meta/hooks/configure 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-snapd-service/meta/hooks/configure 2017-10-27 12:23:38.000000000 +0000 @@ -0,0 +1,14 @@ +#!/bin/sh + +snapctl set service-option=$(snapctl get service-option-source) + +COMMAND=$(snapctl get command) +if [ "$COMMAND" != "" ]; then + if [ "$COMMAND" = "restart" ]; then + snapctl restart test-snapd-service.test-snapd-service + snapctl restart test-snapd-service.test-snapd-other-service + else + snapctl "$COMMAND" test-snapd-service.test-snapd-service + fi +fi +sleep 3 diff -Nru snapd-2.28.5/tests/lib/snaps/test-snapd-service/meta/snap.yaml snapd-2.29.3/tests/lib/snaps/test-snapd-service/meta/snap.yaml --- snapd-2.28.5/tests/lib/snaps/test-snapd-service/meta/snap.yaml 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-snapd-service/meta/snap.yaml 2017-10-27 12:23:38.000000000 +0000 @@ -1,8 +1,10 @@ name: test-snapd-service version: 1.0 apps: - test-snapd-service: - command: bin/start - daemon: simple - reload-command: bin/reload - + test-snapd-service: + command: bin/start + daemon: simple + reload-command: bin/reload + test-snapd-other-service: + command: bin/start-other + daemon: simple diff -Nru snapd-2.28.5/tests/lib/snaps/test-strict-cgroup/bin/read-fb snapd-2.29.3/tests/lib/snaps/test-strict-cgroup/bin/read-fb --- snapd-2.28.5/tests/lib/snaps/test-strict-cgroup/bin/read-fb 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-strict-cgroup/bin/read-fb 2017-11-08 06:21:40.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +cat /dev/fb0 diff -Nru snapd-2.28.5/tests/lib/snaps/test-strict-cgroup/bin/read-kmsg snapd-2.29.3/tests/lib/snaps/test-strict-cgroup/bin/read-kmsg --- snapd-2.28.5/tests/lib/snaps/test-strict-cgroup/bin/read-kmsg 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-strict-cgroup/bin/read-kmsg 2017-11-02 19:49:21.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +head -1 /dev/kmsg diff -Nru snapd-2.28.5/tests/lib/snaps/test-strict-cgroup/meta/snap.yaml snapd-2.29.3/tests/lib/snaps/test-strict-cgroup/meta/snap.yaml --- snapd-2.28.5/tests/lib/snaps/test-strict-cgroup/meta/snap.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps/test-strict-cgroup/meta/snap.yaml 2017-11-02 19:49:21.000000000 +0000 @@ -0,0 +1,14 @@ +name: test-strict-cgroup +version: 1.0 +summary: Basic strict cgroup tester +description: A basic snap declaring a plug to a udev tagged interface + (framebuffer) +confinement: strict + +apps: + read-fb: + command: bin/read-fb + plugs: [framebuffer] + read-kmsg: + command: bin/read-kmsg + plugs: [framebuffer] diff -Nru snapd-2.28.5/tests/lib/snaps.sh snapd-2.29.3/tests/lib/snaps.sh --- snapd-2.28.5/tests/lib/snaps.sh 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/lib/snaps.sh 2017-11-02 19:49:21.000000000 +0000 @@ -8,7 +8,7 @@ # assigned in a separate step to avoid hiding a failure SNAP_DIR="$(dirname "$SNAP_FILE")" if [ ! -f "$SNAP_FILE" ]; then - snapbuild "$SNAP_DIR" "$SNAP_DIR" + snap pack "$SNAP_DIR" "$SNAP_DIR" fi snap install --dangerous "$@" "$SNAP_FILE" } @@ -21,6 +21,10 @@ install_local "$1" --classic } +install_local_jailmode() { + install_local "$1" --jailmode +} + # mksnap_fast creates a snap using a faster compress algorithm (gzip) # than the regular snaps (which are lzma) mksnap_fast() { @@ -39,7 +43,7 @@ local INTERFACE_NAME="$1" cp -ar "$TESTSLIB/snaps/generic-consumer" . sed "s/@INTERFACE@/$INTERFACE_NAME/" generic-consumer/meta/snap.yaml.in > generic-consumer/meta/snap.yaml - snapbuild generic-consumer generic-consumer + snap pack generic-consumer generic-consumer snap install --dangerous generic-consumer/*.snap rm -rf generic-consumer } diff -Nru snapd-2.28.5/tests/main/base-snaps/task.yaml snapd-2.29.3/tests/main/base-snaps/task.yaml --- snapd-2.28.5/tests/main/base-snaps/task.yaml 2017-09-22 07:00:26.000000000 +0000 +++ snapd-2.29.3/tests/main/base-snaps/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -1,17 +1,18 @@ summary: Check that base snaps work +systems: [-opensuse-*] execute: | . $TESTSLIB/snaps.sh echo "Ensure a snap that requires a unavailable base snap can not be installed" - snapbuild $TESTSLIB/snaps/test-snapd-requires-base . + snap pack $TESTSLIB/snaps/test-snapd-requires-base if install_local test-snapd-requires-base; then echo "ERROR: test-snapd-requires-base should not be installable without test-snapd-base" exit 1 fi echo "Ensure a base snap can be installed" - snapbuild $TESTSLIB/snaps/test-snapd-base . + snap pack $TESTSLIB/snaps/test-snapd-base install_local test-snapd-base snap list | MATCH test-snapd-base @@ -33,7 +34,7 @@ # FIXME: we need to pull in test-snapd-base-bare explicitly here for now # once snapd can do that on its own we can remove this # This will be fixed via: - # https://github.com/mvo5/snappy/tree/ensure-core-in-snapstate-with-bases + # https://bugs.launchpad.net/snapstore/+bug/1715824 # snap install --edge test-snapd-base-bare snap install --edge --devmode test-snapd-busybox-static diff -Nru snapd-2.28.5/tests/main/canonical-livepatch/task.yaml snapd-2.29.3/tests/main/canonical-livepatch/task.yaml --- snapd-2.28.5/tests/main/canonical-livepatch/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/main/canonical-livepatch/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,19 @@ +summary: Ensure canonical-livepatch snap works + +# livepatch works only on 16.04 amd64 systems +systems: [ubuntu-16.04-64] + +execute: | + echo "Ensure canonical-livepatch can be installed" + snap install canonical-livepatch + + echo "Wait for it to respond" + for i in $(seq 30); do + if canonical-livepatch status > /dev/null 2>&1 ; then + break + fi + sleep .5 + done + + echo "And ensure we get the expected status" + canonical-livepatch status | MATCH "Machine is not enabled" diff -Nru snapd-2.28.5/tests/main/cgroup-freezer/task.yaml snapd-2.29.3/tests/main/cgroup-freezer/task.yaml --- snapd-2.28.5/tests/main/cgroup-freezer/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/main/cgroup-freezer/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,42 @@ +summary: Each snap process is moved to appropriate freezer cgroup +details: | + This test creates a snap process that suspends itself and ensures that it + placed into the appropriate hierarchy under the freezer cgroup. +prepare: | + . $TESTSLIB/snaps.sh + install_local test-snapd-sh +execute: | + # Start a "sleep" process in the background + test-snapd-sh -c 'touch $SNAP_DATA/1.stamp && exec sleep 1h' & + pid1=$! + # Ensure that snap-confine has finished its task and that the snap process + # is active. Note that we don't want to wait forever either. + for i in $(seq 30); do + test -e /var/snap/test-snapd-sh/current/1.stamp && break + sleep 0.1 + done + # While the process is alive its PID can be seen in the tasks file of the + # control group. + cat /sys/fs/cgroup/freezer/snap.test-snapd-sh/tasks | MATCH "$pid1" + + # Start a second process so that we can check adding tasks to an existing + # control group. + test-snapd-sh -c 'touch $SNAP_DATA/2.stamp && exec sleep 1h' & + pid2=$! + for i in $(seq 30); do + test -e /var/snap/test-snapd-sh/current/2.stamp && break + sleep 0.1 + done + cat /sys/fs/cgroup/freezer/snap.test-snapd-sh/tasks | MATCH "$pid2" + + # When the process terminates the control group is updated and the task no + # longer registers there. + kill "$pid1" + wait -n || true # wait returns the exit code and we kill the process + cat /sys/fs/cgroup/freezer/snap.test-snapd-sh/tasks | MATCH -v "$pid1" + + kill "$pid2" + wait -n || true # same as above + cat /sys/fs/cgroup/freezer/snap.test-snapd-sh/tasks | MATCH -v "$pid2" +restore: | + rmdir /sys/fs/cgroup/freezer/snap.test-snapd-sh || true diff -Nru snapd-2.28.5/tests/main/classic-confinement/task.yaml snapd-2.29.3/tests/main/classic-confinement/task.yaml --- snapd-2.28.5/tests/main/classic-confinement/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/classic-confinement/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -8,7 +8,7 @@ prepare: | . $TESTSLIB/snaps.sh - snapbuild "$TESTSLIB/snaps/$CLASSIC_SNAP/" . + snap pack "$TESTSLIB/snaps/$CLASSIC_SNAP/" execute: | echo "Check that classic snaps work only with --classic" diff -Nru snapd-2.28.5/tests/main/classic-confinement-not-supported/task.yaml snapd-2.29.3/tests/main/classic-confinement-not-supported/task.yaml --- snapd-2.28.5/tests/main/classic-confinement-not-supported/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/classic-confinement-not-supported/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -7,7 +7,7 @@ prepare: | . $TESTSLIB/snaps.sh - snapbuild "$TESTSLIB/snaps/$CLASSIC_SNAP/" . + snap pack "$TESTSLIB/snaps/$CLASSIC_SNAP/" execute: | . $TESTSLIB/strings.sh diff -Nru snapd-2.28.5/tests/main/classic-custom-device-reg/task.yaml snapd-2.29.3/tests/main/classic-custom-device-reg/task.yaml --- snapd-2.28.5/tests/main/classic-custom-device-reg/task.yaml 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/main/classic-custom-device-reg/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -10,7 +10,7 @@ fi . "$TESTSLIB/systemd.sh" - snapbuild "$TESTSLIB/snaps/classic-gadget" . + snap pack "$TESTSLIB/snaps/classic-gadget" snap download "--$CORE_CHANNEL" core "$TESTSLIB/reset.sh" --keep-stopped diff -Nru snapd-2.28.5/tests/main/classic-firstboot/task.yaml snapd-2.29.3/tests/main/classic-firstboot/task.yaml --- snapd-2.28.5/tests/main/classic-firstboot/task.yaml 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/main/classic-firstboot/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -8,7 +8,7 @@ exit fi - snapbuild "$TESTSLIB/snaps/basic" . + snap pack "$TESTSLIB/snaps/basic" snap download "--$CORE_CHANNEL" core "$TESTSLIB/reset.sh" --keep-stopped diff -Nru snapd-2.28.5/tests/main/completion/alias.exp snapd-2.29.3/tests/main/completion/alias.exp --- snapd-2.28.5/tests/main/completion/alias.exp 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/main/completion/alias.exp 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,13 @@ +source lib.exp0 + +# alias completes binaries, not just any istalled snap +# (here, "core" isn't offered for completion) +chat "snap alias \t" "test-snapd-tools." +chat "env myenv\n" "test-snapd-tools.env as myenv" + +# unalias completes aliases and snaps that have aliases +chat "snap unalias \t\t" "myenv *test-snapd-tools" +chat "m\t\n" "test-snapd-tools.env as myenv" + +cancel +brexit diff -Nru snapd-2.28.5/tests/main/completion/buy.exp snapd-2.29.3/tests/main/completion/buy.exp --- snapd-2.28.5/tests/main/completion/buy.exp 2016-12-08 15:14:07.000000000 +0000 +++ snapd-2.29.3/tests/main/completion/buy.exp 2017-10-23 06:17:27.000000000 +0000 @@ -1,8 +1,8 @@ source lib.exp0 # buy completes remote snaps only -chat "snap buy \t\t" "snap buy ??$" -chat "test-\t\t" "test-assumes*test-snapd-tools" +# chat "snap buy \t\t" "snap buy ??$" +chat "snap buy test-snapd-t\t\t" "test-snapd-thumbnailer*test-snapd-tools" cancel brexit diff -Nru snapd-2.28.5/tests/main/completion/info.exp snapd-2.29.3/tests/main/completion/info.exp --- snapd-2.28.5/tests/main/completion/info.exp 2016-12-08 15:14:07.000000000 +0000 +++ snapd-2.29.3/tests/main/completion/info.exp 2017-10-23 06:17:27.000000000 +0000 @@ -1,10 +1,11 @@ source lib.exp0 -# info completes directories and snap files, and locally installed snaps -chat "snap inf\t\t\t" "bar.snap*core*testdir/" +# info completes directories and snap files +chat "snap inf\t./\t\t" "bar.snap*testdir/" +cancel -# with 3+ chars, also remote snaps -chat "test-\t\t" "test-assumes" +# also remote snaps +chat "snap info test-\t\t" "test-assumes" cancel brexit diff -Nru snapd-2.28.5/tests/main/completion/install.exp snapd-2.29.3/tests/main/completion/install.exp --- snapd-2.28.5/tests/main/completion/install.exp 2016-12-08 15:14:07.000000000 +0000 +++ snapd-2.29.3/tests/main/completion/install.exp 2017-10-23 06:17:27.000000000 +0000 @@ -1,10 +1,11 @@ source lib.exp0 # install completes directories and snaps -chat "snap ins\t\t\t" "bar.snap*testdir/" +chat "snap ins\t./\t\t" "bar.snap*testdir/" +cancel -# install with 3+ chars also completes store stuff -chat "tes\t\t\t" "test-assumes" true +# install also completes store stuff +chat "snap install tes\t\t\t" "test-assumes" true # TODO: don't list things already installed: chat "" "test-snapd-tools" true chat "" "testdir/" diff -Nru snapd-2.28.5/tests/main/completion/task.yaml snapd-2.29.3/tests/main/completion/task.yaml --- snapd-2.28.5/tests/main/completion/task.yaml 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/main/completion/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -5,7 +5,19 @@ # ppc64el disabled because of https://bugs.launchpad.net/snappy/+bug/1655594 - -ubuntu-*-ppc64el +environment: + NAMES: /var/cache/snapd/names + prepare: | + systemctl stop snapd.service snapd.socket + [ -e "$NAMES" ] && mv "$NAMES" "$NAMES.orig" + cat >"$NAMES" < +#include -int main() +int main(int argc, char **argv) { - printf("Hello Classic!\n"); + if (argc == 1) { + printf("Hello Classic!\n"); + } else { + printf("TMPDIR=%s\n", getenv("TMPDIR")); + } return 0; } diff -Nru snapd-2.28.5/tests/main/econnreset/task.yaml snapd-2.29.3/tests/main/econnreset/task.yaml --- snapd-2.28.5/tests/main/econnreset/task.yaml 2017-10-13 07:54:42.000000000 +0000 +++ snapd-2.29.3/tests/main/econnreset/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -1,7 +1,7 @@ summary: Ensure that ECONNRESET is handled restore: | echo "Remove the firewall rule again" - iptables -D OUTPUT -m owner --uid-owner $(id -u test) -j REJECT -p tcp --reject-with tcp-reset + iptables -D OUTPUT -m owner --uid-owner $(id -u test) -j REJECT -p tcp --reject-with tcp-reset || true rm -f test-snapd-huge_* diff -Nru snapd-2.28.5/tests/main/failover/task.yaml snapd-2.29.3/tests/main/failover/task.yaml --- snapd-2.28.5/tests/main/failover/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/failover/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -77,7 +77,7 @@ eval ${INJECT_FAILURE} # repack new target snap - snapbuild $BUILD_DIR/unpack . && mv ${TARGET_SNAP_NAME}_*.snap failing.snap + snap pack $BUILD_DIR/unpack && mv ${TARGET_SNAP_NAME}_*.snap failing.snap # install new target snap chg_id=$(snap install --dangerous failing.snap --no-wait) diff -Nru snapd-2.28.5/tests/main/i18n/task.yaml snapd-2.29.3/tests/main/i18n/task.yaml --- snapd-2.28.5/tests/main/i18n/task.yaml 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/tests/main/i18n/task.yaml 2017-10-27 12:23:38.000000000 +0000 @@ -13,4 +13,14 @@ fi echo "Ensure that i18n works" - LANG=de_DE.UTF-8 snap changes everything | MATCH "Ja, ja, allerdings." + LANG=de snap changes everything | MATCH "Ja, ja, allerdings." + + echo "Basic smoke test to ensure no locale causes crashes nor warnings" + for p in /usr/share/locale/*; do + out=$( LANG=$(basename $p) snap 2>&1 >/dev/null ) + if [ -n "$out" ]; then + echo "$p" + echo "$out" + exit 1 + fi + done diff -Nru snapd-2.28.5/tests/main/install-sideload/task.yaml snapd-2.29.3/tests/main/install-sideload/task.yaml --- snapd-2.28.5/tests/main/install-sideload/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/install-sideload/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -3,7 +3,7 @@ prepare: | for snap in basic test-snapd-tools basic-desktop test-snapd-devmode do - snapbuild $TESTSLIB/snaps/$snap . + snap pack $TESTSLIB/snaps/$snap done environment: diff -Nru snapd-2.28.5/tests/main/install-snaps/task.yaml snapd-2.29.3/tests/main/install-snaps/task.yaml --- snapd-2.28.5/tests/main/install-snaps/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/install-snaps/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -66,6 +66,17 @@ SNAP/solc: solc SNAP/vault: vault +prepare: | + cp /etc/systemd/system/snapd.service.d/local.conf /etc/systemd/system/snapd.service.d/local.conf.bak + sed 's/SNAPD_CONFIGURE_HOOK_TIMEOUT=.*s/SNAPD_CONFIGURE_HOOK_TIMEOUT=180s/g' -i /etc/systemd/system/snapd.service.d/local.conf + systemctl daemon-reload + systemctl restart snapd.socket + +restore: | + mv /etc/systemd/system/snapd.service.d/local.conf.bak /etc/systemd/system/snapd.service.d/local.conf + systemctl daemon-reload + systemctl restart snapd.socket + execute: | . "$TESTSLIB/snaps.sh" diff -Nru snapd-2.28.5/tests/main/interfaces-browser-support/task.yaml snapd-2.29.3/tests/main/interfaces-browser-support/task.yaml --- snapd-2.28.5/tests/main/interfaces-browser-support/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/interfaces-browser-support/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -34,7 +34,7 @@ echo "Given a snap declaring a plug on browser-support with allow-sandbox set to $ALLOW_SANDBOX is installed" cp -ar "$TESTSLIB/snaps/browser-support-consumer" . sed "s/@ALLOW_SANDBOX@/$ALLOW_SANDBOX/" browser-support-consumer/meta/snap.yaml.in > browser-support-consumer/meta/snap.yaml - snapbuild browser-support-consumer browser-support-consumer + snap pack browser-support-consumer browser-support-consumer snap install --dangerous browser-support-consumer/*.snap rm -rf browser-support-consumer diff -Nru snapd-2.28.5/tests/main/interfaces-cli/task.yaml snapd-2.29.3/tests/main/interfaces-cli/task.yaml --- snapd-2.28.5/tests/main/interfaces-cli/task.yaml 2016-09-09 06:45:37.000000000 +0000 +++ snapd-2.29.3/tests/main/interfaces-cli/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -7,7 +7,7 @@ prepare: | echo "Given a snap with the $PLUG plug is installed" - snapbuild $TESTSLIB/snaps/$SNAP_NAME . + snap pack $TESTSLIB/snaps/$SNAP_NAME snap install --dangerous $SNAP_FILE restore: | diff -Nru snapd-2.28.5/tests/main/interfaces-cups-control/task.yaml snapd-2.29.3/tests/main/interfaces-cups-control/task.yaml --- snapd-2.28.5/tests/main/interfaces-cups-control/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/interfaces-cups-control/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -61,7 +61,7 @@ echo "Then the snap command is able to print files" echo "Hello World" > $TEST_FILE test-snapd-cups-control-consumer.lpr $TEST_FILE - while ! test -e $HOME/PDF/test_file*.pdf; do sleep 1; done + while ! test -e $HOME/PDF/*.pdf; do sleep 1; done if [ "$(snap debug confinement)" = partial ] ; then exit 0 diff -Nru snapd-2.28.5/tests/main/interfaces-firewall-control/task.yaml snapd-2.29.3/tests/main/interfaces-firewall-control/task.yaml --- snapd-2.28.5/tests/main/interfaces-firewall-control/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/interfaces-firewall-control/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -24,7 +24,7 @@ prepare: | echo "Given a snap declaring a plug on the firewall-control interface is installed" - snapbuild $TESTSLIB/snaps/firewall-control-consumer . + snap pack $TESTSLIB/snaps/firewall-control-consumer snap install --dangerous firewall-control-consumer_1.0_all.snap echo "And a service is listening" diff -Nru snapd-2.28.5/tests/main/interfaces-hardware-observe/task.yaml snapd-2.29.3/tests/main/interfaces-hardware-observe/task.yaml --- snapd-2.28.5/tests/main/interfaces-hardware-observe/task.yaml 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/main/interfaces-hardware-observe/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -11,7 +11,7 @@ prepare: | echo "Given a snap declaring a plug on the hardware-observe interface is installed" - snapbuild $TESTSLIB/snaps/hardware-observe-consumer . + snap pack $TESTSLIB/snaps/hardware-observe-consumer snap install --dangerous hardware-observe-consumer_1.0_all.snap restore: | diff -Nru snapd-2.28.5/tests/main/interfaces-home/task.yaml snapd-2.29.3/tests/main/interfaces-home/task.yaml --- snapd-2.28.5/tests/main/interfaces-home/task.yaml 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/main/interfaces-home/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -18,7 +18,7 @@ prepare: | echo "Given a snap declaring the home plug is installed" - snapbuild $TESTSLIB/snaps/home-consumer . + snap pack $TESTSLIB/snaps/home-consumer snap install --dangerous $SNAP_FILE echo "And there is a readable file in HOME" diff -Nru snapd-2.28.5/tests/main/interfaces-hooks/task.yaml snapd-2.29.3/tests/main/interfaces-hooks/task.yaml --- snapd-2.28.5/tests/main/interfaces-hooks/task.yaml 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/main/interfaces-hooks/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -2,8 +2,8 @@ prepare: | echo "Build test hooks package" - snapbuild $TESTSLIB/snaps/basic-iface-hooks-consumer . - snapbuild $TESTSLIB/snaps/basic-iface-hooks-producer . + snap pack $TESTSLIB/snaps/basic-iface-hooks-consumer + snap pack $TESTSLIB/snaps/basic-iface-hooks-producer snap install --dangerous basic-iface-hooks-consumer_1.0_all.snap snap install --dangerous basic-iface-hooks-producer_1.0_all.snap diff -Nru snapd-2.28.5/tests/main/interfaces-locale-control/task.yaml snapd-2.29.3/tests/main/interfaces-locale-control/task.yaml --- snapd-2.28.5/tests/main/interfaces-locale-control/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/interfaces-locale-control/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -23,7 +23,7 @@ fi echo "Given a snap declaring a plug on the locale-control interface is installed" - snapbuild $TESTSLIB/snaps/locale-control-consumer . + snap pack $TESTSLIB/snaps/locale-control-consumer snap install --dangerous locale-control-consumer_1.0_all.snap mv /etc/default/locale locale.back cat > /etc/default/locale <${PWD}/call.error; then + echo "Expected permission error calling nmcli method with disconnected plug" + exit 1 + fi + MATCH "Permission denied" < call.error diff -Nru snapd-2.28.5/tests/main/interfaces-network-observe/task.yaml snapd-2.29.3/tests/main/interfaces-network-observe/task.yaml --- snapd-2.28.5/tests/main/interfaces-network-observe/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/interfaces-network-observe/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -21,7 +21,7 @@ . "$TESTSLIB/systemd.sh" echo "Given a snap declaring a plug on the network-observe interface is installed" - snapbuild $TESTSLIB/snaps/network-observe-consumer . + snap pack $TESTSLIB/snaps/network-observe-consumer snap install --dangerous network-observe-consumer_1.0_all.snap echo "And a network service is up" diff -Nru snapd-2.28.5/tests/main/interfaces-process-control/task.yaml snapd-2.29.3/tests/main/interfaces-process-control/task.yaml --- snapd-2.28.5/tests/main/interfaces-process-control/task.yaml 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/main/interfaces-process-control/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -14,7 +14,7 @@ prepare: | echo "Given a snap declaring a plug on the process-control interface is installed" - snapbuild $TESTSLIB/snaps/process-control-consumer . + snap pack $TESTSLIB/snaps/process-control-consumer snap install --dangerous process-control-consumer_1.0_all.snap restore: | diff -Nru snapd-2.28.5/tests/main/interfaces-udev/task.yaml snapd-2.29.3/tests/main/interfaces-udev/task.yaml --- snapd-2.28.5/tests/main/interfaces-udev/task.yaml 2016-09-09 06:45:37.000000000 +0000 +++ snapd-2.29.3/tests/main/interfaces-udev/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -12,7 +12,7 @@ prepare: | echo "Given a snap declaring a slot with associated udev rules is installed" - snapbuild $TESTSLIB/snaps/modem-manager-consumer . + snap pack $TESTSLIB/snaps/modem-manager-consumer snap install --dangerous modem-manager-consumer_1.0_all.snap restore: | diff -Nru snapd-2.28.5/tests/main/login/unsuccessful_login.exp snapd-2.29.3/tests/main/login/unsuccessful_login.exp --- snapd-2.28.5/tests/main/login/unsuccessful_login.exp 2017-08-18 13:48:10.000000000 +0000 +++ snapd-2.29.3/tests/main/login/unsuccessful_login.exp 2017-10-27 06:13:24.000000000 +0000 @@ -6,7 +6,7 @@ send "wrong-password\n" expect { - -re "not\[ \n\r\]*correct" { + -re "invalid\[ \n\r\]*credentials" { exit 0 } default { exit 1 diff -Nru snapd-2.28.5/tests/main/lxd/task.yaml snapd-2.29.3/tests/main/lxd/task.yaml --- snapd-2.28.5/tests/main/lxd/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/lxd/task.yaml 2017-11-02 19:49:38.000000000 +0000 @@ -4,6 +4,9 @@ # currently nor on ubuntu 14.04 systems: [ubuntu-16*, ubuntu-core-*] +# lxd downloads can be quite slow +kill-timeout: 25m + restore: | if [[ $(ls -1 "$GOHOME"/snapd_*.deb | wc -l || echo 0) -eq 0 ]]; then exit @@ -80,3 +83,7 @@ echo "Ensure we can use snapd inside lxd" lxd.lxc exec my-ubuntu snap install test-snapd-tools lxd.lxc exec my-ubuntu test-snapd-tools.echo from-the-inside | MATCH from-the-inside + + echo "Install lxd-demo server to exercise the lxd interface" + snap install lxd-demo-server + snap connect lxd-demo-server:lxd lxd:lxd diff -Nru snapd-2.28.5/tests/main/op-remove/task.yaml snapd-2.29.3/tests/main/op-remove/task.yaml --- snapd-2.28.5/tests/main/op-remove/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/op-remove/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -13,7 +13,7 @@ } echo "Given two revisions of a snap have been installed" - snapbuild $TESTSLIB/snaps/basic . + snap pack $TESTSLIB/snaps/basic snap install --dangerous basic_1.0_all.snap snap install --dangerous basic_1.0_all.snap diff -Nru snapd-2.28.5/tests/main/refresh-undo/task.yaml snapd-2.29.3/tests/main/refresh-undo/task.yaml --- snapd-2.28.5/tests/main/refresh-undo/task.yaml 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/tests/main/refresh-undo/task.yaml 2017-11-02 19:49:43.000000000 +0000 @@ -3,6 +3,9 @@ When a snap is refreshed and the refresh fails, the undo code had a bug that removed the security confinement (LP: #1637981) +# trusty has unreliable journalctl output for unknown reasonsg +systems: [-ubuntu-14.04-*] + environment: SNAP_NAME: test-snapd-service SNAP_NAME_GOOD: ${SNAP_NAME}-v1-good @@ -12,8 +15,8 @@ prepare: | echo "Given a good (v1) and a bad (v2) snap" - snapbuild $TESTSLIB/snaps/$SNAP_NAME_GOOD . - snapbuild $TESTSLIB/snaps/$SNAP_NAME_BAD . + snap pack $TESTSLIB/snaps/$SNAP_NAME_GOOD + snap pack $TESTSLIB/snaps/$SNAP_NAME_BAD debug: | journalctl -u snap.test-snapd-service.service.service diff -Nru snapd-2.28.5/tests/main/revert/task.yaml snapd-2.29.3/tests/main/revert/task.yaml --- snapd-2.28.5/tests/main/revert/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/revert/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -66,6 +66,9 @@ echo "Then the new version is installed" snap list | MATCH "test-snapd-tools +[0-9]+\.[0-9]+\+fake1" + echo "And the snap runs" + test-snapd-tools.echo hello|MATCH hello + echo "When a revert is made" snap revert test-snapd-tools @@ -79,6 +82,9 @@ echo "And the snap runs confined" snap list|MATCH 'test-snapd-tools.* -$' + echo "And the still snap runs" + test-snapd-tools.echo hello|MATCH hello + echo "And a new revert fails" if snap revert test-snapd-tools; then echo "A revert on an already reverted snap should fail" diff -Nru snapd-2.28.5/tests/main/revert-devmode/task.yaml snapd-2.29.3/tests/main/revert-devmode/task.yaml --- snapd-2.28.5/tests/main/revert-devmode/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/revert-devmode/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -67,15 +67,17 @@ echo "Then the old version is active" snap list | MATCH 'test-snapd-tools +[0-9]+\.[0-9]+ ' - echo "And the snap runs confined" - snap list|MATCH 'test-snapd-tools .* -' + echo "And the snap runs in devmode" + snap list|MATCH 'test-snapd-tools .* devmode' echo "When the latest revision is installed again" snap remove --revision=$LATEST test-snapd-tools - snap refresh --devmode --edge test-snapd-tools + snap refresh --edge test-snapd-tools - echo "And revert is made with --devmode flag" - snap revert --devmode test-snapd-tools + if [ "$(snap debug confinement)" = strict ] ; then + echo "And revert is made with --jailmode flag" + snap revert --jailmode test-snapd-tools - echo "Then snap uses devmode" - snap list|MATCH 'test-snapd-tools .* devmode' + echo "Then snap now runs confined (in jailmode, bah)" + snap list|MATCH 'test-snapd-tools .* jailmode' + fi diff -Nru snapd-2.28.5/tests/main/revert-sideload/task.yaml snapd-2.29.3/tests/main/revert-sideload/task.yaml --- snapd-2.28.5/tests/main/revert-sideload/task.yaml 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/main/revert-sideload/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -1,7 +1,7 @@ summary: Checks for snap sideload reverts prepare: | - snapbuild $TESTSLIB/snaps/basic . + snap pack $TESTSLIB/snaps/basic restore: | rm ./basic_1.0_all.snap diff -Nru snapd-2.28.5/tests/main/security-device-cgroups/task.yaml snapd-2.29.3/tests/main/security-device-cgroups/task.yaml --- snapd-2.28.5/tests/main/security-device-cgroups/task.yaml 2017-10-11 17:40:25.000000000 +0000 +++ snapd-2.29.3/tests/main/security-device-cgroups/task.yaml 2017-11-03 05:48:53.000000000 +0000 @@ -40,6 +40,11 @@ if [ -e /dev/nvidia254 ]; then mv /dev/nvidia254 /dev/nvidia254.spread fi + # create uhid device if it doesn't exist + if [ ! -e /dev/uhid ]; then + mknod /dev/uhid c 10 239 + touch /dev/uhid.spread + fi restore: | if [ -e /dev/nvidia0.spread ]; then @@ -54,6 +59,9 @@ if [ -e /dev/nvidia254.spread ]; then mv /dev/nvidia254.spread /dev/nvidia254 fi + if [ -e /dev/uhid.spread ]; then + rm -f /dev/uhid /dev/uhid.spread + fi rm -f /etc/udev/rules.d/70-snap.test-snapd-tools.rules udevadm control --reload-rules udevadm trigger @@ -107,4 +115,7 @@ echo "But nonexisting nvidia devices are not" MATCH -v "c 195:254 rwm" < /sys/fs/cgroup/devices/snap.test-snapd-tools.env/devices.list + echo "But the existing uhid device is in the snap's device cgroup" + MATCH "c 10:239 rwm" < /sys/fs/cgroup/devices/snap.test-snapd-tools.env/devices.list + # TODO: check device unassociated after removing the udev file and rebooting diff -Nru snapd-2.28.5/tests/main/security-device-cgroups-classic/task.yaml snapd-2.29.3/tests/main/security-device-cgroups-classic/task.yaml --- snapd-2.28.5/tests/main/security-device-cgroups-classic/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/main/security-device-cgroups-classic/task.yaml 2017-11-08 06:21:40.000000000 +0000 @@ -0,0 +1,37 @@ +summary: Check that device nodes are available in classic + +details: | + This tests that a framebuffer device is accessible in classic and makes + sure that other devices are still accessible (ie, the cgroup is not in + effect). + +# Disabled on Fedora and Ubuntu Core because they don't support classic +# confinement. +systems: [-fedora-*, -ubuntu-core-*] + +prepare: | + # Create framebuffer device node and give it some content we can verify + # the test snap can read. + if [ ! -e /dev/fb0 ]; then + mknod /dev/fb0 c 29 0 + touch /dev/fb0.spread + fi + + echo "Given a snap declaring a plug on framebuffer is installed in classic" + . $TESTSLIB/snaps.sh + install_local_classic test-classic-cgroup + +restore: | + if [ -e /dev/fb0.spread ]; then + rm -f /dev/fb0 /dev/fb0.spread + fi + +execute: | + . $TESTSLIB/dirs.sh + + # classic snaps don't use 'plugs', so just test the accesses after install + echo "the classic snap can access the framebuffer" + "$SNAP_MOUNT_DIR"/bin/test-classic-cgroup.read-fb 2>&1 | MATCH -v '(Permission denied|Operation not permitted)' + + echo "the classic snap can access other devices" + test "`$SNAP_MOUNT_DIR/bin/test-classic-cgroup.read-kmsg`" diff -Nru snapd-2.28.5/tests/main/security-device-cgroups-devmode/task.yaml snapd-2.29.3/tests/main/security-device-cgroups-devmode/task.yaml --- snapd-2.28.5/tests/main/security-device-cgroups-devmode/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/main/security-device-cgroups-devmode/task.yaml 2017-11-08 06:21:40.000000000 +0000 @@ -0,0 +1,42 @@ +summary: Check that plugged and unplugged device nodes are available in devmode + +details: | + This tests that a framebuffer device is accessible in devmode and makes + sure that other devices not included in the snap's plugged interfaces are + still accessible (ie, the cgroup is not in effect). + +prepare: | + # Create framebuffer device node and give it some content we can verify + # the test snap can read. + if [ ! -e /dev/fb0 ]; then + mknod /dev/fb0 c 29 0 + touch /dev/fb0.spread + fi + + echo "Given a snap declaring a plug on framebuffer is installed in devmode" + . $TESTSLIB/snaps.sh + install_local_devmode test-devmode-cgroup + +restore: | + if [ -e /dev/fb0.spread ]; then + rm -f /dev/fb0 /dev/fb0.spread + fi + +execute: | + . $TESTSLIB/dirs.sh + + echo "And the framebuffer plug is connected" + snap connect test-devmode-cgroup:framebuffer + echo "the devmode snap can access the framebuffer" + "$SNAP_MOUNT_DIR"/bin/test-devmode-cgroup.read-fb 2>&1 | MATCH -v '(Permission denied|Operation not permitted)' + + echo "the devmode snap can access other devices" + test "`$SNAP_MOUNT_DIR/bin/test-devmode-cgroup.read-kmsg`" + + echo "And the framebuffer plug is disconnected" + snap disconnect test-devmode-cgroup:framebuffer + echo "the devmode snap can access the framebuffer" + "$SNAP_MOUNT_DIR"/bin/test-devmode-cgroup.read-fb 2>&1 | MATCH -v '(Permission denied|Operation not permitted)' + + echo "the devmode snap can access other devices" + test "`$SNAP_MOUNT_DIR/bin/test-devmode-cgroup.read-kmsg`" diff -Nru snapd-2.28.5/tests/main/security-device-cgroups-jailmode/task.yaml snapd-2.29.3/tests/main/security-device-cgroups-jailmode/task.yaml --- snapd-2.28.5/tests/main/security-device-cgroups-jailmode/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/main/security-device-cgroups-jailmode/task.yaml 2017-11-08 06:21:40.000000000 +0000 @@ -0,0 +1,45 @@ +summary: Check that plugged and unplugged device nodes are available in jailmode + +details: | + This tests that a framebuffer device is accessible in jailmode and makes + sure that other devices not included in the snap's plugged interfaces are + still accessible (ie, the cgroup is not in effect). + +# None of those systems support strict confinement which is required to formally enable jailmode. +systems: [-fedora-*, -opensuse-*, -debian-unstable-*] + +prepare: | + # Create framebuffer device node and give it some content we can verify + # the test snap can read. + if [ ! -e /dev/fb0 ]; then + mknod /dev/fb0 c 29 0 + touch /dev/fb0.spread + fi + + echo "Given a snap declaring a plug on framebuffer is installed in jailmode" + . $TESTSLIB/snaps.sh + install_local_jailmode test-devmode-cgroup + +restore: | + if [ -e /dev/fb0.spread ]; then + rm -f /dev/fb0 /dev/fb0.spread + fi + +execute: | + . $TESTSLIB/dirs.sh + + echo "And the framebuffer plug is connected" + snap connect test-devmode-cgroup:framebuffer + echo "the jailmode snap can access the framebuffer" + "$SNAP_MOUNT_DIR"/bin/test-devmode-cgroup.read-fb 2>&1 | MATCH -v '(Permission denied|Operation not permitted)' + + echo "the jailmode snap cannot access other devices" + "$SNAP_MOUNT_DIR"/bin/test-devmode-cgroup.read-kmsg 2>&1 | MATCH '(Permission denied|Operation not permitted)' + + echo "And the framebuffer plug is disconnected" + snap disconnect test-devmode-cgroup:framebuffer + echo "the jailmode snap cannot access the framebuffer" + "$SNAP_MOUNT_DIR"/bin/test-devmode-cgroup.read-fb 2>&1 | MATCH '(Permission denied|Operation not permitted)' + + echo "the jailmode snap cannot access other devices" + "$SNAP_MOUNT_DIR"/bin/test-devmode-cgroup.read-kmsg 2>&1 | MATCH '(Permission denied|Operation not permitted)' diff -Nru snapd-2.28.5/tests/main/security-device-cgroups-serial-port/task.yaml snapd-2.29.3/tests/main/security-device-cgroups-serial-port/task.yaml --- snapd-2.28.5/tests/main/security-device-cgroups-serial-port/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/main/security-device-cgroups-serial-port/task.yaml 2017-11-03 05:48:53.000000000 +0000 @@ -0,0 +1,55 @@ +summary: Ensure that the device cgroup works properly for serial-port. + +# We don't run the native kernel on these distributions yet so we can't +# load kernel modules coming from distribution packages yet. +systems: [-fedora-*, -opensuse-*, -debian-unstable-*] + +prepare: | + # create serial devices if they don't exist + if [ ! -e /dev/ttyS4 ]; then + mknod /dev/ttyS4 c 4 68 + touch /dev/ttyS4.spread + fi + +restore: | + if [ -e /dev/ttyS4.spread ]; then + rm -f /dev/ttyS4 /dev/ttyS4.spread + fi + + udevadm control --reload-rules + udevadm trigger + +execute: | + echo "Given a snap is installed" + . $TESTSLIB/snaps.sh + install_local test-snapd-tools + + echo "Then the device is not assigned to that snap" + ! udevadm info /dev/ttyS4 | MATCH "E: TAGS=.*snap_test-snapd-tools_env" + + echo "And the device is not shown in the snap device list" + # FIXME: this is, apparently, a layered can of worms. Zyga says he needs to fix it. + if [ -e /sys/fs/cgroup/devices/snap.test-snapd-tools.env/devices.list ]; then + MATCH -v "c 4:68 rwm" < /sys/fs/cgroup/devices/snap.test-snapd-tools.env/devices.list + fi + + echo "=================================================" + + echo "When a udev rule assigning the device to the snap is added" + content="SUBSYSTEM==\"tty\", KERNEL==\"ttyS4\", TAG+=\"snap_test-snapd-tools_env\"" + echo "$content" > /etc/udev/rules.d/70-snap.test-snapd-tools.rules + udevadm control --reload-rules + udevadm settle + udevadm trigger + udevadm settle + + echo "Then the device is shown as assigned to the snap" + udevadm info /dev/ttyS4 | MATCH "E: TAGS=.*snap_test-snapd-tools_env" + + echo "=================================================" + + echo "When a snap command is called" + test-snapd-tools.env + + echo "Then the device is shown in the snap device list" + MATCH "c 4:68 rwm" < /sys/fs/cgroup/devices/snap.test-snapd-tools.env/devices.list diff -Nru snapd-2.28.5/tests/main/security-device-cgroups-strict/task.yaml snapd-2.29.3/tests/main/security-device-cgroups-strict/task.yaml --- snapd-2.28.5/tests/main/security-device-cgroups-strict/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/main/security-device-cgroups-strict/task.yaml 2017-11-08 06:21:40.000000000 +0000 @@ -0,0 +1,44 @@ +summary: Check that plugged and unplugged device nodes are available in strict + +details: | + This tests that a framebuffer device is accessible in strict and makes + sure that other devices not included in the snap's plugged interfaces are + still accessible (ie, the cgroup is not in effect). + +systems: [-fedora-*, -opensuse-*,-debian-unstable-*] + +prepare: | + # Create framebuffer device node and give it some content we can verify + # the test snap can read. + if [ ! -e /dev/fb0 ]; then + mknod /dev/fb0 c 29 0 + touch /dev/fb0.spread + fi + + echo "Given a snap declaring a plug on framebuffer is installed in strict" + . $TESTSLIB/snaps.sh + install_local test-strict-cgroup + +restore: | + if [ -e /dev/fb0.spread ]; then + rm -f /dev/fb0 /dev/fb0.spread + fi + +execute: | + . $TESTSLIB/dirs.sh + + echo "And the framebuffer plug is connected" + snap connect test-strict-cgroup:framebuffer + echo "the strict snap can access the framebuffer" + "$SNAP_MOUNT_DIR"/bin/test-strict-cgroup.read-fb 2>&1 | MATCH -v '(Permission denied|Operation not permitted)' + + echo "the strict snap cannot access other devices" + "$SNAP_MOUNT_DIR"/bin/test-strict-cgroup.read-kmsg 2>&1 | MATCH '(Permission denied|Operation not permitted)' + + echo "And the framebuffer plug is disconnected" + snap disconnect test-strict-cgroup:framebuffer + echo "the strict snap cannot access the framebuffer" + "$SNAP_MOUNT_DIR"/bin/test-strict-cgroup.read-fb 2>&1 | MATCH '(Permission denied|Operation not permitted)' + + echo "the strict snap cannot access other devices" + "$SNAP_MOUNT_DIR"/bin/test-strict-cgroup.read-kmsg 2>&1 | MATCH '(Permission denied|Operation not permitted)' diff -Nru snapd-2.28.5/tests/main/security-devpts/pts.exp snapd-2.29.3/tests/main/security-devpts/pts.exp --- snapd-2.28.5/tests/main/security-devpts/pts.exp 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/security-devpts/pts.exp 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -#!/usr/bin/expect -f - -set timeout 20 - -spawn bash -send "ls /dev/pts\n" -expect "\[0-9]*ptmx" {} timeout { exit 1 } - -# Launch app and checks contents of /dev/pts, should have only ptmx -spawn su -l -c "$env(SNAP_MOUNT_DIR)/bin/test-snapd-tools.sh" test -expect "bash-4.3\\$" {} timeout { exit 1 } -send "ls /dev/pts\n" -expect { - timeout { exit 1 } - "\[0-9]" { exit 1 } - "ptmx" -} - -# From within confined app, open a pty -send "python3\n" -expect ">>>" {} timeout { exit 1 } -send "import os, pty, sys\n" -send "os.listdir('/dev/pts')\n" -expect "\['ptmx'\]" {} timeout { exit 1 } -send "pty.openpty()\n" -send "os.listdir('/dev/pts')\n" -expect "\['0', 'ptmx'\]" {} timeout { exit 1 } -send "sys.exit(0)\n" -expect "bash-4.3\\$" {} timeout { exit 1 } - -# From with confined app, verify pty was closed (assumes that the last program -# closes it properly, which the above does) -send "ls /dev/pts\n" -expect { - timeout { exit 1 } - "\[0-9]" { exit 1 } - "ptmx" -} diff -Nru snapd-2.28.5/tests/main/security-devpts/task.yaml snapd-2.29.3/tests/main/security-devpts/task.yaml --- snapd-2.28.5/tests/main/security-devpts/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/security-devpts/task.yaml 2017-11-09 09:07:17.000000000 +0000 @@ -1,18 +1,35 @@ summary: Ensure that the basic devpts security rules are in place. -# ppc64el disabled because of https://bugs.launchpad.net/snappy/+bug/1655594 -systems: [-ubuntu-core-16-*, -ubuntu-*-ppc64el] +execute: | + if [ "$(snap debug confinement)" = none ] ; then + exit 0 + fi -prepare: | echo "Given a basic snap is installed" . $TESTSLIB/snaps.sh - install_local test-snapd-tools + install_local test-snapd-devpts -execute: | - if [ "$(snap debug confinement)" = none ] ; then - exit 0 + CONNECTED_PATTERN=":physical-memory-observe +test-snapd-devpts" + DISCONNECTED_PATTERN="\- +test-snapd-devpts:physical-memory-observe" + + echo "When no plugs are not connected" + if snap interfaces | MATCH "$CONNECTED_PATTERN" ; then + snap disconnect test-snapd-devpts:physical-memory-observe + snap interfaces | MATCH "$DISCONNECTED_PATTERN" fi - echo "Then the pts device follows confinement rules" - . $TESTSLIB/dirs.sh - expect -d -f pts.exp + echo "Then can openpty" + test-snapd-devpts.openpty | MATCH PASS + + echo "Then can access slave PTY" + test-snapd-devpts.useptmx | MATCH PASS + + echo "When a udev tagging plug is connected" + snap connect test-snapd-devpts:physical-memory-observe + snap interfaces | MATCH "$CONNECTED_PATTERN" + + echo "Then can openpty" + test-snapd-devpts.openpty | MATCH PASS + + echo "Then can access slave PTY" + test-snapd-devpts.useptmx | MATCH PASS diff -Nru snapd-2.28.5/tests/main/security-private-tmp/task.yaml snapd-2.29.3/tests/main/security-private-tmp/task.yaml --- snapd-2.28.5/tests/main/security-private-tmp/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/security-private-tmp/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -15,7 +15,7 @@ mkdir -p $SNAP_INSTALL_DIR cp -ra $TESTSLIB/snaps/test-snapd-tools/* $SNAP_INSTALL_DIR sed -i 's/test-snapd-tools/not-test-snapd-tools/g' $SNAP_INSTALL_DIR/meta/snap.yaml - snapbuild $SNAP_INSTALL_DIR . + snap pack $SNAP_INSTALL_DIR snap install --dangerous not-test-snapd-tools_1.0_all.snap restore: | diff -Nru snapd-2.28.5/tests/main/security-profiles/task.yaml snapd-2.29.3/tests/main/security-profiles/task.yaml --- snapd-2.28.5/tests/main/security-profiles/task.yaml 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/main/security-profiles/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -1,7 +1,7 @@ summary: Check security profile generation for apps and hooks. prepare: | - snapbuild $TESTSLIB/snaps/basic-hooks . + snap pack $TESTSLIB/snaps/basic-hooks restore: | rm basic-hooks_1.0_all.snap diff -Nru snapd-2.28.5/tests/main/snap-confine/task.yaml snapd-2.29.3/tests/main/snap-confine/task.yaml --- snapd-2.28.5/tests/main/snap-confine/task.yaml 2017-10-13 21:25:17.000000000 +0000 +++ snapd-2.29.3/tests/main/snap-confine/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -17,7 +17,16 @@ execute: | . $TESTSLIB/dirs.sh + echo "Simulating broken current symlink for core" + mv $SNAP_MOUNT_DIR/core/current $SNAP_MOUNT_DIR/core/current.renamed + + if test-snapd-tools.echo hello 2>snap-confine.stderr; then + echo "test-snapd-tools.echo should fail to run, test broken" + fi + cat snap-confine.stderr | MATCH 'cannot locate the core or legacy core snap \(current symlink missing\?\):' + echo "Test nvidia device fix" + mv $SNAP_MOUNT_DIR/core/current.renamed $SNAP_MOUNT_DIR/core/current # For https://github.com/snapcore/snapd/pull/4042 echo "Simulate nvidia device tags" mkdir -p /run/udev/tags/snap_test-snapd-tools_echo diff -Nru snapd-2.28.5/tests/main/snapctl/task.yaml snapd-2.29.3/tests/main/snapctl/task.yaml --- snapd-2.28.5/tests/main/snapctl/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/snapctl/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -1,7 +1,7 @@ summary: Check that `snapctl` can be run from within hooks prepare: | - snapbuild $TESTSLIB/snaps/snapctl-hooks . + snap pack $TESTSLIB/snaps/snapctl-hooks snap install --dangerous snapctl-hooks_1.0_all.snap restore: | diff -Nru snapd-2.28.5/tests/main/snapctl-from-snap/task.yaml snapd-2.29.3/tests/main/snapctl-from-snap/task.yaml --- snapd-2.28.5/tests/main/snapctl-from-snap/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/snapctl-from-snap/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -3,7 +3,7 @@ prepare: | snap install --devmode jq echo "Build basic test package" - snapbuild $TESTSLIB/snaps/snapctl-from-snap . + snap pack $TESTSLIB/snaps/snapctl-from-snap restore: | rm snapctl-from-snap_1.0_all.snap diff -Nru snapd-2.28.5/tests/main/snapctl-services/task.yaml snapd-2.29.3/tests/main/snapctl-services/task.yaml --- snapd-2.28.5/tests/main/snapctl-services/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/main/snapctl-services/task.yaml 2017-11-03 16:15:11.000000000 +0000 @@ -0,0 +1,73 @@ +summary: Check that own services can be controlled by snapctl + +kill-timeout: 3m +environment: + SERVICEOPTIONFILE: /var/snap/test-snapd-service/current/service-option + +restore: | + rm -f $SERVICEOPTIONFILE + +execute: | + echo "The test is disabled for now until the functionality is restored" + exit 0 + + wait_for_service() { + retry=5 + while ! snap services $1 | MATCH $2; do + retry=$(( retry - 1 )) + if [ $retry -le 0 ]; then + echo "Failed to match the status of service $1, expected: $2" + exit 1 + fi + sleep 1 + done + } + + wait_for_file_change() { + retry=5 + while ! cat $1 | MATCH $2; do + retry=$(( retry - 1 )) + if [ $retry -le 0 ]; then + echo "Failed to match the content of file $1, expected: $2" + exit 1 + fi + sleep 1 + done + } + + echo "When the service snap is installed" + . $TESTSLIB/snaps.sh + install_local test-snapd-service + + echo "We can see it running" + wait_for_service "test-snapd-service.test-snapd-service" " active" + + echo "When we stop the service via configure hook" + snap set test-snapd-service command=stop + + echo "It's stopped" + wait_for_service "test-snapd-service.test-snapd-service" " inactive" + + echo "When we start the service via configure hook" + snap set test-snapd-service command=start + + echo "It's running again" + wait_for_service "test-snapd-service.test-snapd-service" " active" + + echo "When we stop it again" + snap set test-snapd-service command=stop + + echo "It's stopped" + wait_for_service "test-snapd-service.test-snapd-service" " inactive" + + echo "And then restart" + snap set test-snapd-service service-option-source=foo command=restart + + echo "It's running" + wait_for_service "test-snapd-service.test-snapd-service" " active" + + echo "And restart command was executed as part of configure hook change" + snap tasks --last=configure|MATCH -z "restart of .test-snapd-service.test-snapd-service.+restart of .test-snapd-service.test-snapd-other-service" + + echo "And service could get the new service-option set from the hook" + wait_for_file_change $SERVICEOPTIONFILE "^foo$" diff -Nru snapd-2.28.5/tests/main/snap-disconnect/task.yaml snapd-2.29.3/tests/main/snap-disconnect/task.yaml --- snapd-2.28.5/tests/main/snap-disconnect/task.yaml 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/tests/main/snap-disconnect/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -7,7 +7,7 @@ prepare: | echo "Install a test snap" - snapbuild $TESTSLIB/snaps/home-consumer . + snap pack $TESTSLIB/snaps/home-consumer snap install --dangerous $SNAP_FILE restore: | diff -Nru snapd-2.28.5/tests/main/snapd-reexec/task.yaml snapd-2.29.3/tests/main/snapd-reexec/task.yaml --- snapd-2.28.5/tests/main/snapd-reexec/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/snapd-reexec/task.yaml 2017-11-02 19:49:32.000000000 +0000 @@ -79,7 +79,7 @@ echo "Ensure a core refresh restart snapd" prev_core=$(snap list | awk "/^core / {print(\$3)}") snap install --dangerous /var/lib/snapd/snaps/core_${prev_core}.snap - journalctl | MATCH "Requested daemon restart" + snap change --last=install | MATCH "Requested daemon restart" echo "Ensure the right snapd (from the new core) is running" now_core=$(snap list | awk "/^core / {print(\$3)}") diff -Nru snapd-2.28.5/tests/main/snap-env/task.yaml snapd-2.29.3/tests/main/snap-env/task.yaml --- snapd-2.28.5/tests/main/snap-env/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/snap-env/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -1,6 +1,6 @@ summary: inspect all the set environment variables prefixed with SNAP_ and XDG_ prepare: | - snapbuild $TESTSLIB/snaps/test-snapd-tools . + snap pack $TESTSLIB/snaps/test-snapd-tools snap install --dangerous test-snapd-tools_1.0_all.snap restore: | rm -f *.snap @@ -44,6 +44,9 @@ MATCH "^EXTRA_CACHE_DIR=$HOME/snap/test-snapd-tools/x1/.cache" < extra-vars.txt test $(wc -l < extra-vars.txt) -eq 4 + echo "Ensure that TMPDIR is not passed through to a confined snap" + TMPDIR=/foobar test-snapd-tools.env | grep -qv ^TMPDIR= + echo "Ensure that SNAP, PATH and HOME are what we expect" MATCH "^SNAP=/snap/test-snapd-tools/x1$" < misc-vars.txt MATCH '^PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games$' < misc-vars.txt diff -Nru snapd-2.28.5/tests/main/snap-get/task.yaml snapd-2.29.3/tests/main/snap-get/task.yaml --- snapd-2.28.5/tests/main/snap-get/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/snap-get/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -4,16 +4,16 @@ snap install --devmode jq echo "Build basic test package (without hooks)" - snapbuild $TESTSLIB/snaps/basic . + snap pack $TESTSLIB/snaps/basic snap install --dangerous basic_1.0_all.snap echo "Build package with hook to run snapctl set" - snapbuild $TESTSLIB/snaps/snapctl-hooks . + snap pack $TESTSLIB/snaps/snapctl-hooks snap install --dangerous snapctl-hooks_1.0_all.snap restore: | - rm basic_1.0_all.snap - rm snapctl-hooks_1.0_all.snap + rm -f basic_1.0_all.snap + rm -f snapctl-hooks_1.0_all.snap execute: | echo "Test that snap get fails on a snap without any hooks" @@ -22,6 +22,20 @@ exit 1 fi + echo "Test that getting root document without any configuration produces an error with list format" + if output=$(snap get snapctl-hooks 2>&1); then + echo "snap get didn't fail as expected" + exit 1 + fi + expected="error: snap \"snapctl-hooks\" has no configuration" + if [ "$output" != "$expected" ]; then + echo "Expected '$expected' error, but it was '$output'" + exit 1 + fi + + echo "Test that getting root document without any configuration works for json output" + snap get snapctl-hooks -d | MATCH "^{}$" + echo "Test that values set via snapctl can be obtained via snap get" if ! snap set snapctl-hooks command=test-snapctl-set-foo; then echo "snap set unexpectedly failed" @@ -46,6 +60,22 @@ exit 1 fi + echo "Test that keys of json documents can be obtained via snap get" + if ! snap set snapctl-hooks command=test-snapctl-set-bar-doc; then + echo "snap set unexpectedly failed" + exit 1 + fi + snap get snapctl-hooks bar 2>&1 | MATCH -z "WARNING" + snap get snapctl-hooks -l bar 2>&1 | MATCH -z "^Key.*Value.*bar.a.*{\.\.\.}.*bar.b.*3" + snap get snapctl-hooks -d bar | MATCH -z "^{.*\"bar\": {.*\"a\": {.*\"aa\": 1,.*\"ab\": 2.*},.*\"b\": 3.*}.*}" + + snap get snapctl-hooks bar.a.aa | MATCH "^1$" + snap get snapctl-hooks bar.a.ab | MATCH "^2$" + + echo "Test that root document can be obtained via snap get" + snap get snapctl-hooks -l 2>&1 | MATCH -z "^Key.*Value.*bar.*{\.\.\.}.*command.*test-snapctl-set-bar-doc.*foo.*bar" + snap get snapctl-hooks -d | MATCH -z "^{.*\"bar\": {.*\"a\": {.*\"aa\": 1,.*\"ab\": 2.*},.*\"b\": 3.*}.*,.*\"command\": \"test-snapctl-set-bar-doc\",.*\"foo\": \"bar\".*}" + echo "Test number formats" if ! snap set snapctl-hooks command=test-get-int intnumber=1234567890 intnumber2="{\"a\":9876543210}"; then echo "snap set unexpectedly failed" diff -Nru snapd-2.28.5/tests/main/snap-info/task.yaml snapd-2.29.3/tests/main/snap-info/task.yaml --- snapd-2.28.5/tests/main/snap-info/task.yaml 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/main/snap-info/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -4,7 +4,7 @@ . $TESTSLIB/pkgdb.sh distro_install_package python3-yaml - snapbuild $TESTSLIB/snaps/basic . + snap pack $TESTSLIB/snaps/basic snap install test-snapd-tools snap install --channel beta --devmode test-snapd-devmode diff -Nru snapd-2.28.5/tests/main/snap-repair/task.yaml snapd-2.29.3/tests/main/snap-repair/task.yaml --- snapd-2.28.5/tests/main/snap-repair/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/snap-repair/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -17,6 +17,6 @@ systemctl list-timers | MATCH snapd.snap-repair.timer echo "Check that snap-repair can be run" - "${LIBEXECDIR}"/snapd/snap-repair run | MATCH "run is not implemented yet" + "${LIBEXECDIR}"/snapd/snap-repair run diff -Nru snapd-2.28.5/tests/main/snap-run/task.yaml snapd-2.29.3/tests/main/snap-run/task.yaml --- snapd-2.28.5/tests/main/snap-run/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/main/snap-run/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,9 @@ +summary: Check that `snap run` runs + +prepare: | + . $TESTSLIB/snaps.sh + install_local basic-run + +execute: | + echo "Test that snap run use environments" + basic-run.echo-data | MATCH ^/var/snap diff -Nru snapd-2.28.5/tests/main/snap-run-hook/task.yaml snapd-2.29.3/tests/main/snap-run-hook/task.yaml --- snapd-2.28.5/tests/main/snap-run-hook/task.yaml 2017-09-20 07:05:01.000000000 +0000 +++ snapd-2.29.3/tests/main/snap-run-hook/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -9,14 +9,11 @@ prepare: | echo "Build test hooks package" - snapbuild $TESTSLIB/snaps/basic-hooks . + snap pack $TESTSLIB/snaps/basic-hooks snap install --dangerous basic-hooks_1.0_all.snap restore: | rm basic-hooks_1.0_all.snap - -restore: | - rm basic-hooks_1.0_all.snap execute: | # Note that `snap run` doesn't exit non-zero if the hook is missing, so we diff -Nru snapd-2.28.5/tests/main/snap-set/task.yaml snapd-2.29.3/tests/main/snap-set/task.yaml --- snapd-2.28.5/tests/main/snap-set/task.yaml 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/main/snap-set/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -2,14 +2,14 @@ prepare: | echo "Build basic test package (without hooks)" - snapbuild $TESTSLIB/snaps/basic . + snap pack $TESTSLIB/snaps/basic snap install --dangerous basic_1.0_all.snap echo "Build failing hooks package" - snapbuild $TESTSLIB/snaps/failing-config-hooks . + snap pack $TESTSLIB/snaps/failing-config-hooks echo "Build package with hook to run snapctl set" - snapbuild $TESTSLIB/snaps/snapctl-hooks . + snap pack $TESTSLIB/snaps/snapctl-hooks snap install --dangerous snapctl-hooks_1.0_all.snap restore: | diff -Nru snapd-2.28.5/tests/main/snap-userd/task.yaml snapd-2.29.3/tests/main/snap-userd/task.yaml --- snapd-2.28.5/tests/main/snap-userd/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/main/snap-userd/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -13,7 +13,6 @@ . "$TESTSLIB/pkgdb.sh" rm -f dbus.env umount -f /usr/bin/xdg-open || true - umount -f $SNAP_MOUNT_DIR/core/current/usr/bin/xdg-open || true execute: | . "$TESTSLIB/pkgdb.sh" @@ -42,19 +41,9 @@ echo "$@" > /tmp/xdg-open-output EOF chmod +x /tmp/xdg-open + touch /usr/bin/xdg-open mount --bind /tmp/xdg-open /usr/bin/xdg-open - # Until necessary changes landed in the core snap we need - # to create a custom one which points to the new service - # name io.snapcraft.Launcher where the current core - # snap still uses com.canonical.Launcher. - cat << 'EOF' > /tmp/xdg-open-core - #!/bin/sh - dbus-send --print-reply --session --dest=io.snapcraft.Launcher /io/snapcraft/Launcher io.snapcraft.Launcher.OpenURL string:"$1" - EOF - chmod +x /tmp/xdg-open-core - mount --bind /tmp/xdg-open-core $SNAP_MOUNT_DIR/core/current/usr/bin/xdg-open - ensure_xdg_open_output() { rm -f /tmp/xdg-open-output export DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS diff -Nru snapd-2.28.5/tests/main/systemd-service/task.yaml snapd-2.29.3/tests/main/systemd-service/task.yaml --- snapd-2.28.5/tests/main/systemd-service/task.yaml 2017-08-08 06:31:43.000000000 +0000 +++ snapd-2.29.3/tests/main/systemd-service/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -5,7 +5,7 @@ execute: | echo "Given a service snap is installed" - snapbuild $TESTSLIB/snaps/network-bind-consumer . + snap pack $TESTSLIB/snaps/network-bind-consumer snap install --dangerous network-bind-consumer_1.0_all.snap echo "When the service state is reported as active" diff -Nru snapd-2.28.5/tests/main/ubuntu-core-services/task.yaml snapd-2.29.3/tests/main/ubuntu-core-services/task.yaml --- snapd-2.28.5/tests/main/ubuntu-core-services/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/main/ubuntu-core-services/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,17 @@ +summary: Ensure all services on Core are active + +systems: [ubuntu-core-*] + +execute: | + echo "Ensure one-shot services are working" + for oneshot in snapd.autoimport.service snapd.sshd-keygen.service; do + systemctl status $oneshot |MATCH SUCCESS + done + + echo "Ensure services are working" + systemctl status snapd.service |MATCH active + + echo "Ensure timers are working" + for timer in snapd.refresh.timer snapd.snap-repair.timer; do + systemctl is-active $timer + done diff -Nru snapd-2.28.5/tests/main/ubuntu-core-upgrade/task.yaml snapd-2.29.3/tests/main/ubuntu-core-upgrade/task.yaml --- snapd-2.28.5/tests/main/ubuntu-core-upgrade/task.yaml 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/main/ubuntu-core-upgrade/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -16,6 +16,7 @@ prepare: | snap list | awk "/^core / {print(\$3)}" > nextBoot + snap install test-snapd-tools execute: | . $TESTSLIB/boot.sh @@ -62,6 +63,9 @@ # wait for the link task to be done while ! snap change ${chg_id}|grep -q "^Done.*Make snap.*available to the system" ; do sleep 1 ; done + echo "Ensure the test snap still runs" + test-snapd-tools.echo hello | MATCH hello + echo "Ensure the bootloader is correct before reboot" snap list | awk "/^core / {print(\$3)}" > nextBoot test "$(cat prevBoot)" != "$(cat nextBoot)" diff -Nru snapd-2.28.5/tests/main/user-data-handling/task.yaml snapd-2.29.3/tests/main/user-data-handling/task.yaml --- snapd-2.28.5/tests/main/user-data-handling/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/main/user-data-handling/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -0,0 +1,27 @@ +summary: Check that the refresh data copy works. + +execute: | + echo "For an installed snap" + snap install test-snapd-tools + rev=$(snap list|grep test-snapd-tools|tr -s ' '|cut -f3 -d' ') + + echo "That has some user data" + mkdir -p /home/*/snap/test-snapd-tools/$rev/ + touch /home/*/snap/test-snapd-tools/$rev/mock-data + mkdir -p /root/snap/test-snapd-tools/$rev/ + touch /root/snap/test-snapd-tools/$rev/mock-data + + echo "When the snap is refreshed" + snap refresh --channel=edge test-snapd-tools + new_rev=$(snap list|grep test-snapd-tools|tr -s ' '|cut -f3 -d' ') + + echo "Then the user data gets copied" + test -e /home/*/snap/test-snapd-tools/$new_rev/mock-data + test -e /root/snap/test-snapd-tools/$new_rev/mock-data + + echo "When the snap is removed" + snap remove test-snapd-tools + + echo "Then all user data and root data is gone" + ! test -e /home/*/snap/test-snapd-tools/$new_rev/mock-data + ! test -e /root/snap/test-snapd-tools/$new_rev/mock-data diff -Nru snapd-2.28.5/tests/main/writable-areas/task.yaml snapd-2.29.3/tests/main/writable-areas/task.yaml --- snapd-2.28.5/tests/main/writable-areas/task.yaml 2017-01-16 06:45:53.000000000 +0000 +++ snapd-2.29.3/tests/main/writable-areas/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -7,7 +7,7 @@ SNAP_REEXEC/reexec1: 1 prepare: | - snapbuild $TESTSLIB/snaps/data-writer . + snap pack $TESTSLIB/snaps/data-writer restore: | rm data-writer_1.0_all.snap diff -Nru snapd-2.28.5/tests/main/xdg-open-compat/task.yaml snapd-2.29.3/tests/main/xdg-open-compat/task.yaml --- snapd-2.28.5/tests/main/xdg-open-compat/task.yaml 1970-01-01 00:00:00.000000000 +0000 +++ snapd-2.29.3/tests/main/xdg-open-compat/task.yaml 2017-11-09 09:08:20.000000000 +0000 @@ -0,0 +1,107 @@ +summary: Ensure the xdg-open still works in compatibility mode + +description: | + The core snap has a xdg-open binary that sends both to the + new io.snapcraft.Launcher and the old com.canonical.SafeLauncher + dbus session bus. This test ensures the compatibility with + the old launcher is still there for distros that do not re-exec + and still get a new core but still have the old snapd-xdg-open + package. + +# we must have snapd-xdg-open available +systems: [ubuntu-16.04-*] + +# disabled because the "old" snapd-xdg-open is no longer available in the +# archive +manual: true + +environment: + DISPLAY: :0 + XDG_OPEN_OUTPUT: /tmp/xdg-open-output + +restore: | + . "$TESTSLIB/dirs.sh" + . "$TESTSLIB/pkgdb.sh" + rm -f /usr/bin/xdg-open + rm -f $XDG_OPEN_OUTPUT + dpkg -r snapd-xdg-open + rm -f /usr/share/applications/defaults.list + rm -f /usr/share/applications/xdg-open.desktop + +execute: | + . "$TESTSLIB/pkgdb.sh" + . "$TESTSLIB/dirs.sh" + + # echo apt-get is ok as we only run this test on ubuntu + apt download snapd-xdg-open=0.0.0~16.04 + # snapd breaks older version of snapd-xdg-open so we cannot + # install them together. force things to work! + dpkg -i --force-all snapd-xdg-open_*.deb + + # setup some env so that g_app_info_launch_default_for_uri() works + cat > /usr/share/applications/defaults.list < /usr/share/applications/xdg-open.desktop < /usr/bin/xdg-open + #!/bin/sh + echo "$@" > /tmp/xdg-open-output + EOF + chmod +x /usr/bin/xdg-open + + ensure_xdg_open_output() { + rm -f $XDG_OPEN_OUTPUT + export DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS + $SNAP_MOUNT_DIR/core/current/usr/bin/xdg-open "$1" + # xdg-open is async so we need to give it a little bit of time + for i in $(seq 10); do + if [ -e $XDG_OPEN_OUTPUT ]; then + break + fi + sleep .5 + done + test -e $XDG_OPEN_OUTPUT + test "$(cat $XDG_OPEN_OUTPUT)" = "$1" + } + + # Ensure http, https and mailto work + ensure_xdg_open_output "https://snapcraft.io" + ensure_xdg_open_output "http://snapcraft.io" + ensure_xdg_open_output "mailto:talk@snapcraft.io" + + # Ensure other schemes are not passed through + rm $XDG_OPEN_OUTPUT + ! $SNAP_MOUNT_DIR/core/current/usr/bin/xdg-open ftp://snapcraft.io + test ! -e $XDG_OPEN_OUTPUT + ! $SNAP_MOUNT_DIR/core/current/usr/bin/xdg-open aabbcc + test ! -e $XDG_OPEN_OUTPUT diff -Nru snapd-2.28.5/tests/nested/extra-snaps-assertions/task.yaml snapd-2.29.3/tests/nested/extra-snaps-assertions/task.yaml --- snapd-2.28.5/tests/nested/extra-snaps-assertions/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/nested/extra-snaps-assertions/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -64,4 +64,4 @@ execute_remote "snap known model" | MATCH "series: 16" echo "Make sure core has an actual revision" - execute_remote "snap list" | MATCH "^core +[0-9]+\-[0-9.]+ +[0-9]+ +canonical +\-" + execute_remote "snap list" | MATCH "^core +[0-9]+\-[0-9.]+ +[0-9]+ +canonical +" diff -Nru snapd-2.28.5/tests/regression/lp-1641885/task.yaml snapd-2.29.3/tests/regression/lp-1641885/task.yaml --- snapd-2.28.5/tests/regression/lp-1641885/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/regression/lp-1641885/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -11,7 +11,7 @@ "jailmode". This has been reported as https://bugs.launchpad.net/snappy/+bug/1641885 prepare: | - snapbuild "$TESTSLIB/snaps/test-snapd-devmode" . + snap pack "$TESTSLIB/snaps/test-snapd-devmode" echo "Install a test snap that uses 'confinement: devmode' in jailmode" snap install --jailmode --dangerous ./test-snapd-devmode_1.0_all.snap execute: | diff -Nru snapd-2.28.5/tests/regression/lp-1704860/task.yaml snapd-2.29.3/tests/regression/lp-1704860/task.yaml --- snapd-2.28.5/tests/regression/lp-1704860/task.yaml 2017-08-30 09:16:36.000000000 +0000 +++ snapd-2.29.3/tests/regression/lp-1704860/task.yaml 2017-11-09 11:33:41.000000000 +0000 @@ -19,4 +19,7 @@ . $TESTSLIB/snaps.sh install_local_classic test-snapd-classic-confinement # We don't want to see SNAP_DID_REEXEC being set. - snap run --shell test-snapd-classic-confinement ./snap-env-query.sh | MATCH -v 'SNAP_DID_REEXEC=' + if snap run --shell test-snapd-classic-confinement ./snap-env-query.sh | grep 'SNAP_DID_REEXEC='; then + echo "SNAP_DID_REEXEC environment is not reset as it should be" + exit 1 + fi diff -Nru snapd-2.28.5/tests/regression/nmcli/task.yaml snapd-2.29.3/tests/regression/nmcli/task.yaml --- snapd-2.28.5/tests/regression/nmcli/task.yaml 2017-09-14 13:53:11.000000000 +0000 +++ snapd-2.29.3/tests/regression/nmcli/task.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -summary: Checks basic network-manager/nmcli functionality - -description: | - Test regression caused by seccomp argument filtering getting - stricter but the network-manager plug side was not updated - to include "socket AF_NETLINK - NETLINK_KOBJECT_UEVENT" which - is vital for nmcli to work. - -systems: [ubuntu-core-*] - -execute: | - echo "Install network-manager and do basic smoke test" - snap install network-manager - - # using wait_for_service is not enough, systemd considers - # the service active even when it is not (yet) listening to - # dbus - for i in $(seq 300); do - if network-manager.nmcli general; then - break - fi - sleep 1 - done - network-manager.nmcli d show diff -Nru snapd-2.28.5/tests/unit/go/task.yaml snapd-2.29.3/tests/unit/go/task.yaml --- snapd-2.28.5/tests/unit/go/task.yaml 2017-08-24 06:46:40.000000000 +0000 +++ snapd-2.29.3/tests/unit/go/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -1,7 +1,5 @@ summary: Run project static and unit tests -systems: [ubuntu-16.04-64] - restore: | rm -rf /tmp/static-unit-tests @@ -14,7 +12,7 @@ rm -r /tmp/static-unit-tests/src/github.com/snapcore/snapd/vendor/*/ rm -rf /tmp/static-unit-tests/src/github.com/snapcore/snapd/cmd/{autom4te.cache,configure,test-driver,config.status,config.guess,config.sub,config.h.in,compile,install-sh,depcomp,build,missing,aclocal.m4,Makefile,Makefile.in} - su -l -c "cd /tmp/static-unit-tests/src/github.com/snapcore/snapd && GOPATH=/tmp/static-unit-tests ./run-checks --static" test + su -l -c "cd /tmp/static-unit-tests/src/github.com/snapcore/snapd && PATH=$PATH GOPATH=/tmp/static-unit-tests ./run-checks --static" test su -l -c "cd /tmp/static-unit-tests/src/github.com/snapcore/snapd && \ TRAVIS_BUILD_NUMBER=$TRAVIS_BUILD_NUMBER \ TRAVIS_BRANCH=$TRAVIS_BRANCH \ @@ -24,6 +22,7 @@ TRAVIS_JOB_ID=$TRAVIS_JOB_ID \ TRAVIS_REPO_SLUG=$TRAVIS_REPO_SLUG \ TRAVIS_TAG=$TRAVIS_TAG \ + PATH=$PATH \ COVERMODE=$COVERMODE \ TRAVIS=true \ CI=true \ diff -Nru snapd-2.28.5/tests/upgrade/basic/task.yaml snapd-2.29.3/tests/upgrade/basic/task.yaml --- snapd-2.28.5/tests/upgrade/basic/task.yaml 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/tests/upgrade/basic/task.yaml 2017-10-23 06:17:27.000000000 +0000 @@ -30,8 +30,11 @@ echo "Install sanity check snaps with it" snap install test-snapd-tools snap install test-snapd-auto-aliases - if is_classic_confinement_supported; then + # transitional: drop the "snap pack" check once it's released + do_classic=no + if is_classic_confinement_supported && snap pack --help >&/dev/null ; then install_local_classic test-snapd-classic-confinement + do_classic=yes fi echo "Sanity check installs" @@ -54,7 +57,7 @@ snap list | grep test-snapd-tools test-snapd-tools.echo Hello | grep Hello test-snapd-tools.env | grep SNAP_NAME=test-snapd-tools - if is_classic_confinement_supported; then + if [ "$do_classic" = yes ]; then test-snapd-classic-confinement.recurse 5 fi diff -Nru snapd-2.28.5/tests/util/benchmark.sh snapd-2.29.3/tests/util/benchmark.sh --- snapd-2.28.5/tests/util/benchmark.sh 2016-09-28 09:07:27.000000000 +0000 +++ snapd-2.29.3/tests/util/benchmark.sh 2017-10-23 06:17:27.000000000 +0000 @@ -10,8 +10,7 @@ for i in $(seq "$ITERATIONS"); do echo "Running iteration $i of $ITERATIONS" START_TIME=$SECONDS - spread -v "$BACKEND" - if [ "$?" = "0" ]; then + if spread -v "$BACKEND"; then SUCCESSFUL_EXECUTIONS=$((SUCCESSFUL_EXECUTIONS + 1)) ITERATION_TIME=$((SECONDS - START_TIME)) TOTAL_TIME=$((TOTAL_TIME + ITERATION_TIME)) diff -Nru snapd-2.28.5/testutil/dbustest.go snapd-2.29.3/testutil/dbustest.go --- snapd-2.28.5/testutil/dbustest.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/testutil/dbustest.go 2017-10-23 06:17:27.000000000 +0000 @@ -67,3 +67,6 @@ } } + +func (s *DBusTest) SetUpTest(c *C) {} +func (s *DBusTest) TearDownTest(c *C) {} diff -Nru snapd-2.28.5/.travis.yml snapd-2.29.3/.travis.yml --- snapd-2.28.5/.travis.yml 2017-10-10 18:11:24.000000000 +0000 +++ snapd-2.29.3/.travis.yml 2017-10-23 06:17:27.000000000 +0000 @@ -1,7 +1,5 @@ -sudo: required -dist: trusty language: go -go: +go: - 1.6 env: @@ -13,9 +11,12 @@ quiet: true install: - - sudo apt-get update -qq - - sudo apt-get install -qq squashfs-tools xdelta3 - - sudo apt-get install -qq gnupg1 || sudo apt-get install -qq gnupg + - true script: - ./run-checks --spread + +addons: + apt: + packages: + - xdelta3 diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/format.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/format.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/format.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/format.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -package pb - -import ( - "fmt" - "strings" - "time" -) - -type Units int - -const ( - // U_NO are default units, they represent a simple value and are not formatted at all. - U_NO Units = iota - // U_BYTES units are formatted in a human readable way (b, Bb, Mb, ...) - U_BYTES - // U_DURATION units are formatted in a human readable way (3h14m15s) - U_DURATION -) - -func Format(i int64) *formatter { - return &formatter{n: i} -} - -type formatter struct { - n int64 - unit Units - width int - perSec bool -} - -func (f *formatter) Value(n int64) *formatter { - f.n = n - return f -} - -func (f *formatter) To(unit Units) *formatter { - f.unit = unit - return f -} - -func (f *formatter) Width(width int) *formatter { - f.width = width - return f -} - -func (f *formatter) PerSec() *formatter { - f.perSec = true - return f -} - -func (f *formatter) String() (out string) { - switch f.unit { - case U_BYTES: - out = formatBytes(f.n) - case U_DURATION: - d := time.Duration(f.n) - if d > time.Hour*24 { - out = fmt.Sprintf("%dd", d/24/time.Hour) - d -= (d / time.Hour / 24) * (time.Hour * 24) - } - out = fmt.Sprintf("%s%v", out, d) - default: - out = fmt.Sprintf(fmt.Sprintf("%%%dd", f.width), f.n) - } - if f.perSec { - out += "/s" - } - return -} - -// Convert bytes to human readable string. Like a 2 MB, 64.2 KB, 52 B -func formatBytes(i int64) (result string) { - switch { - case i > (1024 * 1024 * 1024 * 1024): - result = fmt.Sprintf("%.02f TB", float64(i)/1024/1024/1024/1024) - case i > (1024 * 1024 * 1024): - result = fmt.Sprintf("%.02f GB", float64(i)/1024/1024/1024) - case i > (1024 * 1024): - result = fmt.Sprintf("%.02f MB", float64(i)/1024/1024) - case i > 1024: - result = fmt.Sprintf("%.02f KB", float64(i)/1024) - default: - result = fmt.Sprintf("%d B", i) - } - result = strings.Trim(result, " ") - return -} diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/LICENSE snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/LICENSE --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/LICENSE 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -Copyright (c) 2012-2015, Sergey Cherepanov -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pb_appengine.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pb_appengine.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pb_appengine.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pb_appengine.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -// +build appengine - -package pb - -import "errors" - -// terminalWidth returns width of the terminal, which is not supported -// and should always failed on appengine classic which is a sandboxed PaaS. -func terminalWidth() (int, error) { - return 0, errors.New("Not supported") -} diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pb.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pb.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pb.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pb.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,441 +0,0 @@ -// Simple console progress bars -package pb - -import ( - "fmt" - "io" - "math" - "strings" - "sync" - "sync/atomic" - "time" - "unicode/utf8" -) - -// Current version -const Version = "1.0.5" - -const ( - // Default refresh rate - 200ms - DEFAULT_REFRESH_RATE = time.Millisecond * 200 - FORMAT = "[=>-]" -) - -// DEPRECATED -// variables for backward compatibility, from now do not work -// use pb.Format and pb.SetRefreshRate -var ( - DefaultRefreshRate = DEFAULT_REFRESH_RATE - BarStart, BarEnd, Empty, Current, CurrentN string -) - -// Create new progress bar object -func New(total int) *ProgressBar { - return New64(int64(total)) -} - -// Create new progress bar object using int64 as total -func New64(total int64) *ProgressBar { - pb := &ProgressBar{ - Total: total, - RefreshRate: DEFAULT_REFRESH_RATE, - ShowPercent: true, - ShowCounters: true, - ShowBar: true, - ShowTimeLeft: true, - ShowFinalTime: true, - Units: U_NO, - ManualUpdate: false, - finish: make(chan struct{}), - currentValue: -1, - mu: new(sync.Mutex), - } - return pb.Format(FORMAT) -} - -// Create new object and start -func StartNew(total int) *ProgressBar { - return New(total).Start() -} - -// Callback for custom output -// For example: -// bar.Callback = func(s string) { -// mySuperPrint(s) -// } -// -type Callback func(out string) - -type ProgressBar struct { - current int64 // current must be first member of struct (https://code.google.com/p/go/issues/detail?id=5278) - - Total int64 - RefreshRate time.Duration - ShowPercent, ShowCounters bool - ShowSpeed, ShowTimeLeft, ShowBar bool - ShowFinalTime bool - Output io.Writer - Callback Callback - NotPrint bool - Units Units - Width int - ForceWidth bool - ManualUpdate bool - AutoStat bool - - // Default width for the time box. - UnitsWidth int - TimeBoxWidth int - - finishOnce sync.Once //Guards isFinish - finish chan struct{} - isFinish bool - - startTime time.Time - startValue int64 - currentValue int64 - - prefix, postfix string - - mu *sync.Mutex - lastPrint string - - BarStart string - BarEnd string - Empty string - Current string - CurrentN string - - AlwaysUpdate bool -} - -// Start print -func (pb *ProgressBar) Start() *ProgressBar { - pb.startTime = time.Now() - pb.startValue = pb.current - if pb.Total == 0 { - pb.ShowTimeLeft = false - pb.ShowPercent = false - pb.AutoStat = false - } - if !pb.ManualUpdate { - pb.Update() // Initial printing of the bar before running the bar refresher. - go pb.refresher() - } - return pb -} - -// Increment current value -func (pb *ProgressBar) Increment() int { - return pb.Add(1) -} - -// Get current value -func (pb *ProgressBar) Get() int64 { - c := atomic.LoadInt64(&pb.current) - return c -} - -// Set current value -func (pb *ProgressBar) Set(current int) *ProgressBar { - return pb.Set64(int64(current)) -} - -// Set64 sets the current value as int64 -func (pb *ProgressBar) Set64(current int64) *ProgressBar { - atomic.StoreInt64(&pb.current, current) - return pb -} - -// Add to current value -func (pb *ProgressBar) Add(add int) int { - return int(pb.Add64(int64(add))) -} - -func (pb *ProgressBar) Add64(add int64) int64 { - return atomic.AddInt64(&pb.current, add) -} - -// Set prefix string -func (pb *ProgressBar) Prefix(prefix string) *ProgressBar { - pb.prefix = prefix - return pb -} - -// Set postfix string -func (pb *ProgressBar) Postfix(postfix string) *ProgressBar { - pb.postfix = postfix - return pb -} - -// Set custom format for bar -// Example: bar.Format("[=>_]") -// Example: bar.Format("[\x00=\x00>\x00-\x00]") // \x00 is the delimiter -func (pb *ProgressBar) Format(format string) *ProgressBar { - var formatEntries []string - if len(format) == 5 { - formatEntries = strings.Split(format, "") - } else { - formatEntries = strings.Split(format, "\x00") - } - if len(formatEntries) == 5 { - pb.BarStart = formatEntries[0] - pb.BarEnd = formatEntries[4] - pb.Empty = formatEntries[3] - pb.Current = formatEntries[1] - pb.CurrentN = formatEntries[2] - } - return pb -} - -// Set bar refresh rate -func (pb *ProgressBar) SetRefreshRate(rate time.Duration) *ProgressBar { - pb.RefreshRate = rate - return pb -} - -// Set units -// bar.SetUnits(U_NO) - by default -// bar.SetUnits(U_BYTES) - for Mb, Kb, etc -func (pb *ProgressBar) SetUnits(units Units) *ProgressBar { - pb.Units = units - return pb -} - -// Set max width, if width is bigger than terminal width, will be ignored -func (pb *ProgressBar) SetMaxWidth(width int) *ProgressBar { - pb.Width = width - pb.ForceWidth = false - return pb -} - -// Set bar width -func (pb *ProgressBar) SetWidth(width int) *ProgressBar { - pb.Width = width - pb.ForceWidth = true - return pb -} - -// End print -func (pb *ProgressBar) Finish() { - //Protect multiple calls - pb.finishOnce.Do(func() { - close(pb.finish) - pb.write(atomic.LoadInt64(&pb.current)) - if !pb.NotPrint { - fmt.Println() - } - pb.isFinish = true - }) -} - -// End print and write string 'str' -func (pb *ProgressBar) FinishPrint(str string) { - pb.Finish() - fmt.Println(str) -} - -// implement io.Writer -func (pb *ProgressBar) Write(p []byte) (n int, err error) { - n = len(p) - pb.Add(n) - return -} - -// implement io.Reader -func (pb *ProgressBar) Read(p []byte) (n int, err error) { - n = len(p) - pb.Add(n) - return -} - -// Create new proxy reader over bar -// Takes io.Reader or io.ReadCloser -func (pb *ProgressBar) NewProxyReader(r io.Reader) *Reader { - return &Reader{r, pb} -} - -func (pb *ProgressBar) write(current int64) { - width := pb.GetWidth() - - var percentBox, countersBox, timeLeftBox, speedBox, barBox, end, out string - - // percents - if pb.ShowPercent { - var percent float64 - if pb.Total > 0 { - percent = float64(current) / (float64(pb.Total) / float64(100)) - } else { - percent = float64(current) / float64(100) - } - percentBox = fmt.Sprintf(" %6.02f%%", percent) - } - - // counters - if pb.ShowCounters { - current := Format(current).To(pb.Units).Width(pb.UnitsWidth) - if pb.Total > 0 { - total := Format(pb.Total).To(pb.Units).Width(pb.UnitsWidth) - countersBox = fmt.Sprintf(" %s / %s ", current, total) - } else { - countersBox = fmt.Sprintf(" %s / ? ", current) - } - } - - // time left - fromStart := time.Now().Sub(pb.startTime) - currentFromStart := current - pb.startValue - select { - case <-pb.finish: - if pb.ShowFinalTime { - var left time.Duration - if pb.Total > 0 { - left = (fromStart / time.Second) * time.Second - } else { - left = (time.Duration(currentFromStart) / time.Second) * time.Second - } - timeLeftBox = fmt.Sprintf(" %s", left.String()) - } - default: - if pb.ShowTimeLeft && currentFromStart > 0 { - perEntry := fromStart / time.Duration(currentFromStart) - var left time.Duration - if pb.Total > 0 { - left = time.Duration(pb.Total-currentFromStart) * perEntry - left = (left / time.Second) * time.Second - } else { - left = time.Duration(currentFromStart) * perEntry - left = (left / time.Second) * time.Second - } - timeLeft := Format(int64(left)).To(U_DURATION).String() - timeLeftBox = fmt.Sprintf(" %s", timeLeft) - } - } - - if len(timeLeftBox) < pb.TimeBoxWidth { - timeLeftBox = fmt.Sprintf("%s%s", strings.Repeat(" ", pb.TimeBoxWidth-len(timeLeftBox)), timeLeftBox) - } - - // speed - if pb.ShowSpeed && currentFromStart > 0 { - fromStart := time.Now().Sub(pb.startTime) - speed := float64(currentFromStart) / (float64(fromStart) / float64(time.Second)) - speedBox = " " + Format(int64(speed)).To(pb.Units).Width(pb.UnitsWidth).PerSec().String() - } - - barWidth := escapeAwareRuneCountInString(countersBox + pb.BarStart + pb.BarEnd + percentBox + timeLeftBox + speedBox + pb.prefix + pb.postfix) - // bar - if pb.ShowBar { - size := width - barWidth - if size > 0 { - if pb.Total > 0 { - curCount := int(math.Ceil((float64(current) / float64(pb.Total)) * float64(size))) - emptCount := size - curCount - barBox = pb.BarStart - if emptCount < 0 { - emptCount = 0 - } - if curCount > size { - curCount = size - } - if emptCount <= 0 { - barBox += strings.Repeat(pb.Current, curCount) - } else if curCount > 0 { - barBox += strings.Repeat(pb.Current, curCount-1) + pb.CurrentN - } - barBox += strings.Repeat(pb.Empty, emptCount) + pb.BarEnd - } else { - barBox = pb.BarStart - pos := size - int(current)%int(size) - if pos-1 > 0 { - barBox += strings.Repeat(pb.Empty, pos-1) - } - barBox += pb.Current - if size-pos-1 > 0 { - barBox += strings.Repeat(pb.Empty, size-pos-1) - } - barBox += pb.BarEnd - } - } - } - - // check len - out = pb.prefix + countersBox + barBox + percentBox + speedBox + timeLeftBox + pb.postfix - if escapeAwareRuneCountInString(out) < width { - end = strings.Repeat(" ", width-utf8.RuneCountInString(out)) - } - - // and print! - pb.mu.Lock() - pb.lastPrint = out + end - pb.mu.Unlock() - switch { - case pb.isFinish: - return - case pb.Output != nil: - fmt.Fprint(pb.Output, "\r"+out+end) - case pb.Callback != nil: - pb.Callback(out + end) - case !pb.NotPrint: - fmt.Print("\r" + out + end) - } -} - -// GetTerminalWidth - returns terminal width for all platforms. -func GetTerminalWidth() (int, error) { - return terminalWidth() -} - -func (pb *ProgressBar) GetWidth() int { - if pb.ForceWidth { - return pb.Width - } - - width := pb.Width - termWidth, _ := terminalWidth() - if width == 0 || termWidth <= width { - width = termWidth - } - - return width -} - -// Write the current state of the progressbar -func (pb *ProgressBar) Update() { - c := atomic.LoadInt64(&pb.current) - if pb.AlwaysUpdate || c != pb.currentValue { - pb.write(c) - pb.currentValue = c - } - if pb.AutoStat { - if c == 0 { - pb.startTime = time.Now() - pb.startValue = 0 - } else if c >= pb.Total && pb.isFinish != true { - pb.Finish() - } - } -} - -func (pb *ProgressBar) String() string { - return pb.lastPrint -} - -// Internal loop for refreshing the progressbar -func (pb *ProgressBar) refresher() { - for { - select { - case <-pb.finish: - return - case <-time.After(pb.RefreshRate): - pb.Update() - } - } -} - -type window struct { - Row uint16 - Col uint16 - Xpixel uint16 - Ypixel uint16 -} diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pb_nix.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pb_nix.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pb_nix.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pb_nix.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -// +build linux darwin freebsd netbsd openbsd dragonfly -// +build !appengine - -package pb - -import "syscall" - -const sysIoctl = syscall.SYS_IOCTL diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pb_solaris.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pb_solaris.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pb_solaris.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pb_solaris.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -// +build solaris -// +build !appengine - -package pb - -const sysIoctl = 54 diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pb_win.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pb_win.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pb_win.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pb_win.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,141 +0,0 @@ -// +build windows - -package pb - -import ( - "errors" - "fmt" - "os" - "sync" - "syscall" - "unsafe" -) - -var tty = os.Stdin - -var ( - kernel32 = syscall.NewLazyDLL("kernel32.dll") - - // GetConsoleScreenBufferInfo retrieves information about the - // specified console screen buffer. - // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx - procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") - - // GetConsoleMode retrieves the current input mode of a console's - // input buffer or the current output mode of a console screen buffer. - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx - getConsoleMode = kernel32.NewProc("GetConsoleMode") - - // SetConsoleMode sets the input mode of a console's input buffer - // or the output mode of a console screen buffer. - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx - setConsoleMode = kernel32.NewProc("SetConsoleMode") - - // SetConsoleCursorPosition sets the cursor position in the - // specified console screen buffer. - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx - setConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") -) - -type ( - // Defines the coordinates of the upper left and lower right corners - // of a rectangle. - // See - // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686311(v=vs.85).aspx - smallRect struct { - Left, Top, Right, Bottom int16 - } - - // Defines the coordinates of a character cell in a console screen - // buffer. The origin of the coordinate system (0,0) is at the top, left cell - // of the buffer. - // See - // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119(v=vs.85).aspx - coordinates struct { - X, Y int16 - } - - word int16 - - // Contains information about a console screen buffer. - // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx - consoleScreenBufferInfo struct { - dwSize coordinates - dwCursorPosition coordinates - wAttributes word - srWindow smallRect - dwMaximumWindowSize coordinates - } -) - -// terminalWidth returns width of the terminal. -func terminalWidth() (width int, err error) { - var info consoleScreenBufferInfo - _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0) - if e != 0 { - return 0, error(e) - } - return int(info.dwSize.X) - 1, nil -} - -func getCursorPos() (pos coordinates, err error) { - var info consoleScreenBufferInfo - _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0) - if e != 0 { - return info.dwCursorPosition, error(e) - } - return info.dwCursorPosition, nil -} - -func setCursorPos(pos coordinates) error { - _, _, e := syscall.Syscall(setConsoleCursorPosition.Addr(), 2, uintptr(syscall.Stdout), uintptr(uint32(uint16(pos.Y))<<16|uint32(uint16(pos.X))), 0) - if e != 0 { - return error(e) - } - return nil -} - -var ErrPoolWasStarted = errors.New("Bar pool was started") - -var echoLocked bool -var echoLockMutex sync.Mutex - -var oldState word - -func lockEcho() (quit chan int, err error) { - echoLockMutex.Lock() - defer echoLockMutex.Unlock() - if echoLocked { - err = ErrPoolWasStarted - return - } - echoLocked = true - - if _, _, e := syscall.Syscall(getConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&oldState)), 0); e != 0 { - err = fmt.Errorf("Can't get terminal settings: %v", e) - return - } - - newState := oldState - const ENABLE_ECHO_INPUT = 0x0004 - const ENABLE_LINE_INPUT = 0x0002 - newState = newState & (^(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT)) - if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(newState), 0); e != 0 { - err = fmt.Errorf("Can't set terminal settings: %v", e) - return - } - return -} - -func unlockEcho() (err error) { - echoLockMutex.Lock() - defer echoLockMutex.Unlock() - if !echoLocked { - return - } - echoLocked = false - if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(oldState), 0); e != 0 { - err = fmt.Errorf("Can't set terminal settings") - } - return -} diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pb_x.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pb_x.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pb_x.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pb_x.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,110 +0,0 @@ -// +build linux darwin freebsd netbsd openbsd solaris dragonfly -// +build !appengine - -package pb - -import ( - "errors" - "fmt" - "os" - "os/signal" - "runtime" - "sync" - "syscall" - "unsafe" -) - -const ( - TIOCGWINSZ = 0x5413 - TIOCGWINSZ_OSX = 1074295912 -) - -var tty *os.File - -var ErrPoolWasStarted = errors.New("Bar pool was started") - -var echoLocked bool -var echoLockMutex sync.Mutex - -func init() { - var err error - tty, err = os.Open("/dev/tty") - if err != nil { - tty = os.Stdin - } -} - -// terminalWidth returns width of the terminal. -func terminalWidth() (int, error) { - w := new(window) - tio := syscall.TIOCGWINSZ - if runtime.GOOS == "darwin" { - tio = TIOCGWINSZ_OSX - } - res, _, err := syscall.Syscall(sysIoctl, - tty.Fd(), - uintptr(tio), - uintptr(unsafe.Pointer(w)), - ) - if int(res) == -1 { - return 0, err - } - return int(w.Col), nil -} - -var oldState syscall.Termios - -func lockEcho() (quit chan int, err error) { - echoLockMutex.Lock() - defer echoLockMutex.Unlock() - if echoLocked { - err = ErrPoolWasStarted - return - } - echoLocked = true - - fd := tty.Fd() - if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 { - err = fmt.Errorf("Can't get terminal settings: %v", e) - return - } - - newState := oldState - newState.Lflag &^= syscall.ECHO - newState.Lflag |= syscall.ICANON | syscall.ISIG - newState.Iflag |= syscall.ICRNL - if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); e != 0 { - err = fmt.Errorf("Can't set terminal settings: %v", e) - return - } - quit = make(chan int, 1) - go catchTerminate(quit) - return -} - -func unlockEcho() (err error) { - echoLockMutex.Lock() - defer echoLockMutex.Unlock() - if !echoLocked { - return - } - echoLocked = false - fd := tty.Fd() - if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 { - err = fmt.Errorf("Can't set terminal settings") - } - return -} - -// listen exit signals and restore terminal state -func catchTerminate(quit chan int) { - sig := make(chan os.Signal, 1) - signal.Notify(sig, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL) - defer signal.Stop(sig) - select { - case <-quit: - unlockEcho() - case <-sig: - unlockEcho() - } -} diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pool.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pool.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pool.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pool.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -// +build linux darwin freebsd netbsd openbsd solaris dragonfly windows - -package pb - -import ( - "sync" - "time" -) - -// Create and start new pool with given bars -// You need call pool.Stop() after work -func StartPool(pbs ...*ProgressBar) (pool *Pool, err error) { - pool = new(Pool) - if err = pool.start(); err != nil { - return - } - pool.Add(pbs...) - return -} - -type Pool struct { - RefreshRate time.Duration - bars []*ProgressBar - quit chan int - finishOnce sync.Once -} - -// Add progress bars. -func (p *Pool) Add(pbs ...*ProgressBar) { - for _, bar := range pbs { - bar.ManualUpdate = true - bar.NotPrint = true - bar.Start() - p.bars = append(p.bars, bar) - } -} - -func (p *Pool) start() (err error) { - p.RefreshRate = DefaultRefreshRate - quit, err := lockEcho() - if err != nil { - return - } - p.quit = make(chan int) - go p.writer(quit) - return -} - -func (p *Pool) writer(finish chan int) { - var first = true - for { - select { - case <-time.After(p.RefreshRate): - if p.print(first) { - p.print(false) - finish <- 1 - return - } - first = false - case <-p.quit: - finish <- 1 - return - } - } -} - -// Restore terminal state and close pool -func (p *Pool) Stop() error { - // Wait until one final refresh has passed. - time.Sleep(p.RefreshRate) - - p.finishOnce.Do(func() { - close(p.quit) - }) - return unlockEcho() -} diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pool_win.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pool_win.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pool_win.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pool_win.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -// +build windows - -package pb - -import ( - "fmt" - "log" -) - -func (p *Pool) print(first bool) bool { - var out string - if !first { - coords, err := getCursorPos() - if err != nil { - log.Panic(err) - } - coords.Y -= int16(len(p.bars)) - coords.X = 0 - - err = setCursorPos(coords) - if err != nil { - log.Panic(err) - } - } - isFinished := true - for _, bar := range p.bars { - if !bar.isFinish { - isFinished = false - } - bar.Update() - out += fmt.Sprintf("\r%s\n", bar.String()) - } - fmt.Print(out) - return isFinished -} diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pool_x.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pool_x.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/pool_x.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/pool_x.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -// +build linux darwin freebsd netbsd openbsd solaris dragonfly - -package pb - -import "fmt" - -func (p *Pool) print(first bool) bool { - var out string - if !first { - out = fmt.Sprintf("\033[%dA", len(p.bars)) - } - isFinished := true - for _, bar := range p.bars { - if !bar.isFinish { - isFinished = false - } - bar.Update() - out += fmt.Sprintf("\r%s\n", bar.String()) - } - fmt.Print(out) - return isFinished -} diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/reader.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/reader.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/reader.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/reader.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -package pb - -import ( - "io" -) - -// It's proxy reader, implement io.Reader -type Reader struct { - io.Reader - bar *ProgressBar -} - -func (r *Reader) Read(p []byte) (n int, err error) { - n, err = r.Reader.Read(p) - r.bar.Add(n) - return -} - -// Close the reader when it implements io.Closer -func (r *Reader) Close() (err error) { - if closer, ok := r.Reader.(io.Closer); ok { - return closer.Close() - } - return -} diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/README.md snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/README.md --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/README.md 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,176 +0,0 @@ -# Terminal progress bar for Go - -Simple progress bar for console programs. - - -## Installation - -``` -go get gopkg.in/cheggaaa/pb.v1 -``` - -## Usage - -```Go -package main - -import ( - "gopkg.in/cheggaaa/pb.v1" - "time" -) - -func main() { - count := 100000 - bar := pb.StartNew(count) - for i := 0; i < count; i++ { - bar.Increment() - time.Sleep(time.Millisecond) - } - bar.FinishPrint("The End!") -} - -``` - -Result will be like this: - -``` -> go run test.go -37158 / 100000 [================>_______________________________] 37.16% 1m11s -``` - -## Customization - -```Go -// create bar -bar := pb.New(count) - -// refresh info every second (default 200ms) -bar.SetRefreshRate(time.Second) - -// show percents (by default already true) -bar.ShowPercent = true - -// show bar (by default already true) -bar.ShowBar = true - -// no counters -bar.ShowCounters = false - -// show "time left" -bar.ShowTimeLeft = true - -// show average speed -bar.ShowSpeed = true - -// sets the width of the progress bar -bar.SetWidth(80) - -// sets the width of the progress bar, but if terminal size smaller will be ignored -bar.SetMaxWidth(80) - -// convert output to readable format (like KB, MB) -bar.SetUnits(pb.U_BYTES) - -// and start -bar.Start() -``` - -## Progress bar for IO Operations - -```go -// create and start bar -bar := pb.New(myDataLen).SetUnits(pb.U_BYTES) -bar.Start() - -// my io.Reader -r := myReader - -// my io.Writer -w := myWriter - -// create proxy reader -reader := bar.NewProxyReader(r) - -// and copy from pb reader -io.Copy(w, reader) - -``` - -```go -// create and start bar -bar := pb.New(myDataLen).SetUnits(pb.U_BYTES) -bar.Start() - -// my io.Reader -r := myReader - -// my io.Writer -w := myWriter - -// create multi writer -writer := io.MultiWriter(w, bar) - -// and copy -io.Copy(writer, r) - -bar.Finish() -``` - -## Custom Progress Bar Look-and-feel - -```go -bar.Format("<.- >") -``` - -## Multiple Progress Bars (experimental and unstable) - -Do not print to terminal while pool is active. - -```go -package main - -import ( - "math/rand" - "sync" - "time" - - "gopkg.in/cheggaaa/pb.v1" -) - -func main() { - // create bars - first := pb.New(200).Prefix("First ") - second := pb.New(200).Prefix("Second ") - third := pb.New(200).Prefix("Third ") - // start pool - pool, err := pb.StartPool(first, second, third) - if err != nil { - panic(err) - } - // update bars - wg := new(sync.WaitGroup) - for _, bar := range []*pb.ProgressBar{first, second, third} { - wg.Add(1) - go func(cb *pb.ProgressBar) { - for n := 0; n < 200; n++ { - cb.Increment() - time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) - } - cb.Finish() - wg.Done() - }(bar) - } - wg.Wait() - // close pool - pool.Stop() -} -``` - -The result will be as follows: - -``` -$ go run example/multiple.go -First 141 / 1000 [===============>---------------------------------------] 14.10 % 44s -Second 139 / 1000 [==============>---------------------------------------] 13.90 % 44s -Third 152 / 1000 [================>--------------------------------------] 15.20 % 40s -``` diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/runecount.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/runecount.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/runecount.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/runecount.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -package pb - -import ( - "regexp" - "unicode/utf8" -) - -// Finds the control character sequences (like colors) -var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d") - -func escapeAwareRuneCountInString(s string) int { - n := utf8.RuneCountInString(s) - for _, sm := range ctrlFinder.FindAllString(s, -1) { - n -= len(sm) - } - return n -} diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/termios_bsd.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/termios_bsd.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/termios_bsd.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/termios_bsd.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -// +build darwin freebsd netbsd openbsd dragonfly -// +build !appengine - -package pb - -import "syscall" - -const ioctlReadTermios = syscall.TIOCGETA -const ioctlWriteTermios = syscall.TIOCSETA diff -Nru snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/termios_nix.go snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/termios_nix.go --- snapd-2.28.5/vendor/gopkg.in/cheggaaa/pb.v1/termios_nix.go 2017-08-16 10:05:25.000000000 +0000 +++ snapd-2.29.3/vendor/gopkg.in/cheggaaa/pb.v1/termios_nix.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -// +build linux solaris -// +build !appengine - -package pb - -const ioctlReadTermios = 0x5401 // syscall.TCGETS -const ioctlWriteTermios = 0x5402 // syscall.TCSETS diff -Nru snapd-2.28.5/vendor/vendor.json snapd-2.29.3/vendor/vendor.json --- snapd-2.28.5/vendor/vendor.json 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/vendor/vendor.json 2017-10-23 07:43:15.000000000 +0000 @@ -151,12 +151,6 @@ "revisionTime": "2016-12-08T18:13:25Z" }, { - "checksumSHA1": "qZxtczIIW7+bjlZ7QbfUJg7RI+s=", - "path": "gopkg.in/cheggaaa/pb.v1", - "revision": "6e9d17711bb763b26b68b3931d47f24c1323abab", - "revisionTime": "2016-08-12T10:57:48Z" - }, - { "checksumSHA1": "mWrTCVjXPLevlqKUqpzSoEM3tu8=", "path": "gopkg.in/macaroon.v1", "revision": "ab3940c6c16510a850e1c2dd628b919f0f3f1464", diff -Nru snapd-2.28.5/wrappers/services_test.go snapd-2.29.3/wrappers/services_test.go --- snapd-2.28.5/wrappers/services_test.go 2017-09-13 14:47:18.000000000 +0000 +++ snapd-2.29.3/wrappers/services_test.go 2017-10-23 06:17:27.000000000 +0000 @@ -38,8 +38,9 @@ ) type servicesTestSuite struct { - tempdir string - prevctlCmd func(...string) ([]byte, error) + tempdir string + + restorer func() } var _ = Suite(&servicesTestSuite{}) @@ -48,23 +49,23 @@ s.tempdir = c.MkDir() dirs.SetRootDir(s.tempdir) - s.prevctlCmd = systemd.SystemctlCmd - systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) { + s.restorer = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { return []byte("ActiveState=inactive\n"), nil - } + }) } func (s *servicesTestSuite) TearDownTest(c *C) { dirs.SetRootDir("") - systemd.SystemctlCmd = s.prevctlCmd + s.restorer() } func (s *servicesTestSuite) TestAddSnapServicesAndRemove(c *C) { var sysdLog [][]string - systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) { + r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { sysdLog = append(sysdLog, cmd) return []byte("ActiveState=inactive\n"), nil - } + }) + defer r() info := snaptest.MockSnap(c, packageHello, contentsHello, &snap.SideInfo{Revision: snap.R(12)}) svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") @@ -87,7 +88,7 @@ } sysdLog = nil - err = wrappers.StopServices(info.Services(), &progress.NullProgress{}) + err = wrappers.StopServices(info.Services(), progress.Null) c.Assert(err, IsNil) c.Assert(sysdLog, HasLen, 2) c.Check(sysdLog, DeepEquals, [][]string{ @@ -96,7 +97,7 @@ }) sysdLog = nil - err = wrappers.RemoveSnapServices(info, &progress.NullProgress{}) + err = wrappers.RemoveSnapServices(info, progress.Null) c.Assert(err, IsNil) c.Check(osutil.FileExists(svcFile), Equals, false) c.Assert(sysdLog, HasLen, 2) @@ -109,14 +110,15 @@ defer restore() var sysdLog [][]string - systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) { + r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { // filter out the "systemctl show" that // StopServices generates if cmd[0] != "show" { sysdLog = append(sysdLog, cmd) } return []byte("ActiveState=active\n"), nil - } + }) + defer r() info := snaptest.MockSnap(c, `name: wat version: 42 @@ -134,7 +136,7 @@ svcFName := "snap.wat.wat.service" - err = wrappers.StopServices(info.Services(), &progress.NullProgress{}) + err = wrappers.StopServices(info.Services(), progress.Null) c.Assert(err, IsNil) c.Check(sysdLog, DeepEquals, [][]string{ @@ -147,10 +149,11 @@ func (s *servicesTestSuite) TestStartServices(c *C) { var sysdLog [][]string - systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) { + r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { sysdLog = append(sysdLog, cmd) return []byte("ActiveState=inactive\n"), nil - } + }) + defer r() info := snaptest.MockSnap(c, packageHello, contentsHello, &snap.SideInfo{Revision: snap.R(12)}) svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") @@ -168,10 +171,11 @@ svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service")) c.Check(svcFiles, HasLen, 0) - systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) { + r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { sysdLog = append(sysdLog, cmd) return nil, nil - } + }) + defer r() info := snaptest.MockSnap(c, packageHello+` svc2: @@ -208,7 +212,7 @@ svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service")) c.Check(svcFiles, HasLen, 0) - systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) { + r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { sysdLog = append(sysdLog, cmd) sdcmd := cmd[0] if len(cmd) >= 2 { @@ -234,7 +238,8 @@ default: panic("unexpected systemctl command " + sdcmd) } - } + }) + defer r() info := snaptest.MockSnap(c, packageHello+` svc2: @@ -267,7 +272,7 @@ c.Check(svcFiles, HasLen, 0) first := true - systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) { + r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { sysdLog = append(sysdLog, cmd) if len(cmd) < 2 { return nil, fmt.Errorf("failed") @@ -281,7 +286,8 @@ } return nil, nil - } + }) + defer r() info := snaptest.MockSnap(c, packageHello+` svc2: @@ -289,7 +295,7 @@ daemon: simple `, contentsHello, &snap.SideInfo{Revision: snap.R(12)}) - err := wrappers.AddSnapServices(info, &progress.NullProgress{}) + err := wrappers.AddSnapServices(info, progress.Null) c.Assert(err, ErrorMatches, "failed") // the services are cleaned up @@ -311,7 +317,7 @@ svc2Name := "snap.hello-snap.svc2.service" numStarts := 0 - systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) { + r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { sysdLog = append(sysdLog, cmd) if len(cmd) >= 2 && cmd[len(cmd)-2] == "start" { numStarts++ @@ -325,7 +331,8 @@ } } return []byte("ActiveState=inactive\n"), nil - } + }) + defer r() info := snaptest.MockSnap(c, packageHello+` svc2: