Repository: code-ready/crc
Pull request #1950 (2021-02-04 11:38:55)

Fix e2e basic feature for windows platform
This PR change scenarios for windows platform on basic feature to adapt the expectations to the current development state.

The e2e pipeline results for windows platform for this branch can be inspected at the internal ci: [windows build 72](https://crcqe-jenkins-csb-codeready.cloud.paas.psi.redhat.com/blue/organizations/jenkins/qe%2Fbundle_baremetal_windows10-brno/detail/bundle_baremetal_windows10-brno/72/).


centos-ci: Can one of the admins verify this patch?
openshift-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/code-ready/crc/pull/1950#" title="Author self-approved">adrianriobo</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **praveenkumar** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @praveenkumar` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=code-ready%2Fcrc).

<details open>
Needs approval from an approver in each of these files:

- **[test/OWNERS](https://github.com/code-ready/crc/blob/master/test/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["praveenkumar"]} -->
openshift-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/code-ready/crc/pull/1950#" title="Author self-approved">adrianriobo</a>*, *<a href="https://github.com/code-ready/crc/pull/1950#pullrequestreview-583306143" title="Approved">jsliacan</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=code-ready%2Fcrc).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[test/OWNERS](https://github.com/code-ready/crc/blob/master/test/OWNERS)~~ [jsliacan]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/go.mod b/go.mod
index 7544a5c95..ea2331290 100644
--- a/go.mod
+++ b/go.mod
@@ -47,6 +47,7 @@ require (
 	github.com/stretchr/testify v1.7.0
 	github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8
 	github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
+	github.com/zalando/go-keyring v0.1.1
 	golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c
 	golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb // indirect
 	golang.org/x/oauth2 v0.0.0-20201203001011-0b49973bad19 // indirect
diff --git a/go.sum b/go.sum
index b385fcae9..ffa3ca1b5 100644
--- a/go.sum
+++ b/go.sum
@@ -231,6 +231,8 @@ github.com/cznic/ql v1.2.0/go.mod h1:FbpzhyZrqr0PVlK6ury+PoW3T0ODUV22OeWIxcaOrSE
 github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ=
 github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
 github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc/go.mod h1:Y1SNZ4dRUOKXshKUbwUapqNncRrho4mkjQebgEHZLj8=
+github.com/danieljoos/wincred v1.1.0 h1:3RNcEpBg4IhIChZdFRSdlQt1QjCp1sMAPIrOnm7Yf8g=
+github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -360,7 +362,9 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
 github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
+github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8=
 github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
+github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
 github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gofrs/flock v0.6.1-0.20180915234121-886344bea079/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
 github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
@@ -861,6 +865,7 @@ github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q
 github.com/storageos/go-api v0.0.0-20180912212459-343b3eff91fc/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@@ -912,6 +917,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
 github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
 github.com/yvasiyarov/gorelic v0.0.7/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
 github.com/yvasiyarov/newrelic_platform_go v0.0.0-20160601141957-9c099fbc30e9/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
+github.com/zalando/go-keyring v0.1.1 h1:w2V9lcx/Uj4l+dzAf1m9s+DJ1O8ROkEHnynonHjTcYE=
+github.com/zalando/go-keyring v0.1.1/go.mod h1:OIC+OZ28XbmwFxU/Rp9V7eKzZjamBJwRzC8UFJH9+L8=
 gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
diff --git a/pkg/crc/cluster/pullsecret.go b/pkg/crc/cluster/pullsecret.go
index daf24985b..450dc826c 100644
--- a/pkg/crc/cluster/pullsecret.go
+++ b/pkg/crc/cluster/pullsecret.go
@@ -1,8 +1,12 @@
 package cluster
 
 import (
+	"bytes"
+	"compress/gzip"
+	"encoding/base64"
 	"errors"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"strings"
 
@@ -13,9 +17,15 @@ import (
 	"github.com/code-ready/crc/pkg/crc/validation"
 	crcversion "github.com/code-ready/crc/pkg/crc/version"
 	crcos "github.com/code-ready/crc/pkg/os"
+	"github.com/zalando/go-keyring"
 	"gopkg.in/AlecAivazis/survey.v1"
 )
 
+const (
+	keyringService = "crc"
+	keyringUser    = "compressed-pull-secret"
+)
+
 type PullSecretLoader interface {
 	Value() (string, error)
 }
@@ -40,7 +50,15 @@ func (loader *interactivePullSecretLoader) Value() (string, error) {
 		return fromNonInteractive, nil
 	}
 
-	return promptUserForSecret()
+	pullSecret, err := promptUserForSecret()
+	if err != nil {
+		return "", err
+	}
+
+	if err := storeInKeyring(pullSecret); err != nil {
+		logging.Warnf("Cannot add pull secret to keyring: %v", err)
+	}
+	return pullSecret, nil
 }
 
 type nonInteractivePullSecretLoader struct {
@@ -66,18 +84,69 @@ func (loader *nonInteractivePullSecretLoader) Value() (string, error) {
 	if loader.path != "" {
 		fromPath, err := loadFile(loader.path)
 		if err == nil {
+			logging.Debugf("Using secret from path %q", loader.path)
 			return fromPath, nil
 		}
 		logging.Debugf("Cannot load secret from path %q: %v", loader.path, err)
 	}
 	fromConfig, err := loadFile(loader.config.Get(cmdConfig.PullSecretFile).AsString())
 	if err == nil {
+		logging.Debugf("Using secret from configuration")
 		return fromConfig, nil
 	}
 	logging.Debugf("Cannot load secret from configuration: %v", err)
+
+	fromKeyring, err := loadFromKeyring()
+	if err == nil {
+		logging.Debugf("Using secret from keyring")
+		return fromKeyring, nil
+	}
+	logging.Debugf("Cannot load secret from keyring: %v", err)
+
 	return "", fmt.Errorf("unable to load pull secret from path %q or from configuration", loader.path)
 }
 
+func loadFromKeyring() (string, error) {
+	pullsecret, err := keyring.Get(keyringService, keyringUser)
+	if err != nil {
+		return "", err
+	}
+	decoded, err := base64.StdEncoding.DecodeString(pullsecret)
+	if err != nil {
+		return "", err
+	}
+	decompressor, err := gzip.NewReader(bytes.NewReader(decoded))
+	if err != nil {
+		return "", err
+	}
+	var b bytes.Buffer
+	// #nosec G110
+	if _, err := io.Copy(&b, decompressor); err != nil {
+		return "", err
+	}
+	if err := decompressor.Close(); err != nil {
+		return "", err
+	}
+	return b.String(), validation.ImagePullSecret(b.String())
+}
+
+func storeInKeyring(pullSecret string) error {
+	var b bytes.Buffer
+	compressor := gzip.NewWriter(&b)
+	if _, err := compressor.Write([]byte(pullSecret)); err != nil {
+		return err
+	}
+	if err := compressor.Close(); err != nil {
+		return err
+	}
+	return keyring.Set(keyringService, keyringUser, base64.StdEncoding.EncodeToString(b.Bytes()))
+}
+
+func ForgetPullSecret() error {
+	_ = keyring.Delete(keyringService, keyringUser)
+	return nil
+}
+
 func loadFile(path string) (string, error) {
 	if path == "" {
 		return "", errors.New("empty path")
diff --git a/pkg/crc/config/viper_config.go b/pkg/crc/config/viper_config.go
index 57b4bbdb8..ba52376a4 100644
--- a/pkg/crc/config/viper_config.go
+++ b/pkg/crc/config/viper_config.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"io/ioutil"
 	"os"
+	"path/filepath"
 	"strings"
 
 	"github.com/spf13/pflag"
@@ -45,7 +46,7 @@ func (c *ViperStorage) Get(key string) interface{} {
 
 func (c *ViperStorage) Set(key string, value interface{}) error {
 	c.viper.Set(key, value)
-	return c.viper.WriteConfigAs(c.configFile)
+	return atomicWrite(c.viper, c.configFile)
 }
 
 func (c *ViperStorage) Unset(key string) error {
@@ -58,8 +59,7 @@ func (c *ViperStorage) Unset(key string) error {
 	if err = c.viper.ReadConfig(bytes.NewReader(bin)); err != nil {
 		return err
 	}
-
-	return c.viper.WriteConfigAs(c.configFile)
+	return atomicWrite(c.viper, c.configFile)
 }
 
 // BindFlagset binds a flagset to their respective config properties
@@ -75,3 +75,22 @@ func ensureConfigFileExists(file string) error {
 	}
 	return err
 }
+
+func atomicWrite(viper *viper.Viper, configFile string) error {
+	ext := filepath.Ext(configFile)
+	pattern := fmt.Sprintf("%s*%s", strings.TrimSuffix(filepath.Base(configFile), ext), ext)
+	tmpFile, err := ioutil.TempFile(filepath.Dir(configFile), pattern)
+	if err != nil {
+		return err
+	}
+	defer func() {
+		_ = os.Remove(tmpFile.Name())
+	}()
+	if err := tmpFile.Close(); err != nil {
+		return err
+	}
+	if err := viper.WriteConfigAs(tmpFile.Name()); err != nil {
+		return err
+	}
+	return os.Rename(tmpFile.Name(), configFile)
+}
diff --git a/pkg/crc/preflight/preflight_checks_common.go b/pkg/crc/preflight/preflight_checks_common.go
index b1f251112..0c0a56eb0 100644
--- a/pkg/crc/preflight/preflight_checks_common.go
+++ b/pkg/crc/preflight/preflight_checks_common.go
@@ -6,6 +6,7 @@ import (
 	"path/filepath"
 
 	"github.com/code-ready/crc/pkg/crc/cache"
+	"github.com/code-ready/crc/pkg/crc/cluster"
 	"github.com/code-ready/crc/pkg/crc/constants"
 	"github.com/code-ready/crc/pkg/crc/logging"
 	"github.com/code-ready/crc/pkg/crc/machine/bundle"
@@ -58,6 +59,11 @@ var genericPreflightChecks = [...]Check{
 		cleanup:            removeOldLogs,
 		flags:              CleanUpOnly,
 	},
+	{
+		cleanupDescription: "Removing pull secret from the keyring",
+		cleanup:            cluster.ForgetPullSecret,
+		flags:              CleanUpOnly,
+	},
 }
 
 func checkBundleExtracted() error {
diff --git a/vendor/github.com/danieljoos/wincred/.gitattributes b/vendor/github.com/danieljoos/wincred/.gitattributes
new file mode 100644
index 000000000..d207b1802
--- /dev/null
+++ b/vendor/github.com/danieljoos/wincred/.gitattributes
@@ -0,0 +1,1 @@
+*.go text eol=lf
diff --git a/vendor/github.com/danieljoos/wincred/.gitignore b/vendor/github.com/danieljoos/wincred/.gitignore
new file mode 100644
index 000000000..836562412
--- /dev/null
+++ b/vendor/github.com/danieljoos/wincred/.gitignore
@@ -0,0 +1,23 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
diff --git a/vendor/github.com/danieljoos/wincred/LICENSE b/vendor/github.com/danieljoos/wincred/LICENSE
new file mode 100644
index 000000000..2f436f1b3
--- /dev/null
+++ b/vendor/github.com/danieljoos/wincred/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Daniel Joos
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/vendor/github.com/danieljoos/wincred/README.md b/vendor/github.com/danieljoos/wincred/README.md
new file mode 100644
index 000000000..064efb763
--- /dev/null
+++ b/vendor/github.com/danieljoos/wincred/README.md
@@ -0,0 +1,98 @@
+wincred
+=======
+
+Go wrapper around the Windows Credential Manager API functions.
+
+![Go](https://github.com/danieljoos/wincred/workflows/Go/badge.svg)
+[![GoDoc](https://godoc.org/github.com/danieljoos/wincred?status.svg)](https://godoc.org/github.com/danieljoos/wincred)
+
+
+Installation
+------------
+
+```Go
+go get github.com/danieljoos/wincred
+```
+
+
+Usage
+-----
+
+See the following examples:
+
+### Create and store a new generic credential object
+```Go
+package main
+
+import (
+    "fmt"
+    "github.com/danieljoos/wincred"
+)
+
+func main() {
+    cred := wincred.NewGenericCredential("myGoApplication")
+    cred.CredentialBlob = []byte("my secret")
+    err := cred.Write()
+    
+    if err != nil {
+        fmt.Println(err)
+    }
+} 
+```
+
+### Retrieve a credential object
+```Go
+package main
+
+import (
+    "fmt"
+    "github.com/danieljoos/wincred"
+)
+
+func main() {
+    cred, err := wincred.GetGenericCredential("myGoApplication")
+    if err == nil {
+        fmt.Println(string(cred.CredentialBlob))
+    }
+} 
+```
+
+### Remove a credential object
+```Go
+package main
+
+import (
+    "fmt"
+    "github.com/danieljoos/wincred"
+)
+
+func main() {
+    cred, err := wincred.GetGenericCredential("myGoApplication")
+    if err != nil {
+        fmt.Println(err)
+        return
+    }
+    cred.Delete()
+} 
+```
+
+### List all available credentials
+```Go
+package main
+
+import (
+    "fmt"
+    "github.com/danieljoos/wincred"
+)
+
+func main() {
+    creds, err := wincred.List()
+    if err != nil {
+        fmt.Println(err)
+        return
+    }
+    for i := range(creds) {
+        fmt.Println(creds[i].TargetName)
+    }
+}
+```
diff --git a/vendor/github.com/danieljoos/wincred/conversion.go b/vendor/github.com/danieljoos/wincred/conversion.go
new file mode 100644
index 000000000..685f90a8f
--- /dev/null
+++ b/vendor/github.com/danieljoos/wincred/conversion.go
@@ -0,0 +1,131 @@
+// +build windows
+
+package wincred
+
+import (
+	"encoding/binary"
+	"reflect"
+	"syscall"
+	"time"
+	"unicode/utf16"
+	"unsafe"
+)
+
+// uf16PtrToString creates a Go string from a pointer to a UTF16 encoded zero-terminated string.
+// Such pointers are returned from the Windows API calls.
+// The function creates a copy of the string.
+func utf16PtrToString(wstr *uint16) string {
+	if wstr != nil {
+		for len := 0; ; len++ {
+			ptr := unsafe.Pointer(uintptr(unsafe.Pointer(wstr)) + uintptr(len)*unsafe.Sizeof(*wstr)) // see https://golang.org/pkg/unsafe/#Pointer (3)
+			if *(*uint16)(ptr) == 0 {
+				return string(utf16.Decode(*(*[]uint16)(unsafe.Pointer(&reflect.SliceHeader{
+					Data: uintptr(unsafe.Pointer(wstr)),
+					Len:  len,
+					Cap:  len,
+				}))))
+			}
+		}
+	}
+	return ""
+}
+
+// utf16ToByte creates a byte array from a given UTF 16 char array.
+func utf16ToByte(wstr []uint16) (result []byte) {
+	result = make([]byte, len(wstr)*2)
+	for i := range wstr {
+		binary.LittleEndian.PutUint16(result[(i*2):(i*2)+2], wstr[i])
+	}
+	return
+}
+
+// utf16FromString creates a UTF16 char array from a string.
+func utf16FromString(str string) []uint16 {
+	return syscall.StringToUTF16(str)
+}
+
+// goBytes copies the given C byte array to a Go byte array (see `C.GoBytes`).
+// This function avoids having cgo as dependency.
+func goBytes(src uintptr, len uint32) []byte {
+	if src == uintptr(0) {
+		return []byte{}
+	}
+	rv := make([]byte, len)
+	copy(rv, *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
+		Data: src,
+		Len:  int(len),
+		Cap:  int(len),
+	})))
+	return rv
+}
+
+// Convert the given CREDENTIAL struct to a more usable structure
+func sysToCredential(cred *sysCREDENTIAL) (result *Credential) {
+	if cred == nil {
+		return nil
+	}
+	result = new(Credential)
+	result.Comment = utf16PtrToString(cred.Comment)
+	result.TargetName = utf16PtrToString(cred.TargetName)
+	result.TargetAlias = utf16PtrToString(cred.TargetAlias)
+	result.UserName = utf16PtrToString(cred.UserName)
+	result.LastWritten = time.Unix(0, cred.LastWritten.Nanoseconds())
+	result.Persist = CredentialPersistence(cred.Persist)
+	result.CredentialBlob = goBytes(cred.CredentialBlob, cred.CredentialBlobSize)
+	result.Attributes = make([]CredentialAttribute, cred.AttributeCount)
+	attrSlice := *(*[]sysCREDENTIAL_ATTRIBUTE)(unsafe.Pointer(&reflect.SliceHeader{
+		Data: cred.Attributes,
+		Len:  int(cred.AttributeCount),
+		Cap:  int(cred.AttributeCount),
+	}))
+	for i, attr := range attrSlice {
+		resultAttr := &result.Attributes[i]
+		resultAttr.Keyword = utf16PtrToString(attr.Keyword)
+		resultAttr.Value = goBytes(attr.Value, attr.ValueSize)
+	}
+	return result
+}
+
+// Convert the given Credential object back to a CREDENTIAL struct, which can be used for calling the
+// Windows APIs
+func sysFromCredential(cred *Credential) (result *sysCREDENTIAL) {
+	if cred == nil {
+		return nil
+	}
+	result = new(sysCREDENTIAL)
+	result.Flags = 0
+	result.Type = 0
+	result.TargetName, _ = syscall.UTF16PtrFromString(cred.TargetName)
+	result.Comment, _ = syscall.UTF16PtrFromString(cred.Comment)
+	result.LastWritten = syscall.NsecToFiletime(cred.LastWritten.UnixNano())
+	result.CredentialBlobSize = uint32(len(cred.CredentialBlob))
+	if len(cred.CredentialBlob) > 0 {
+		result.CredentialBlob = uintptr(unsafe.Pointer(&cred.CredentialBlob[0]))
+	} else {
+		result.CredentialBlob = 0
+	}
+	result.Persist = uint32(cred.Persist)
+	result.AttributeCount = uint32(len(cred.Attributes))
+	attributes := make([]sysCREDENTIAL_ATTRIBUTE, len(cred.Attributes))
+	if len(attributes) > 0 {
+		result.Attributes = uintptr(unsafe.Pointer(&attributes[0]))
+	} else {
+		result.Attributes = 0
+	}
+	for i := range cred.Attributes {
+		inAttr := &cred.Attributes[i]
+		outAttr := &attributes[i]
+		outAttr.Keyword, _ = syscall.UTF16PtrFromString(inAttr.Keyword)
+		outAttr.Flags = 0
+		outAttr.ValueSize = uint32(len(inAttr.Value))
+		if len(inAttr.Value) > 0 {
+			outAttr.Value = uintptr(unsafe.Pointer(&inAttr.Value[0]))
+		} else {
+			outAttr.Value = 0
+		}
+	}
+	result.TargetAlias, _ = syscall.UTF16PtrFromString(cred.TargetAlias)
+	result.UserName, _ = syscall.UTF16PtrFromString(cred.UserName)
+
+	return
+}
diff --git a/vendor/github.com/danieljoos/wincred/conversion_unsupported.go b/vendor/github.com/danieljoos/wincred/conversion_unsupported.go
new file mode 100644
index 000000000..a1ea72075
--- /dev/null
+++ b/vendor/github.com/danieljoos/wincred/conversion_unsupported.go
@@ -0,0 +1,11 @@
+// +build !windows
+
+package wincred
+
+func utf16ToByte(...interface{}) []byte {
+	return nil
+}
+
+func utf16FromString(...interface{}) []uint16 {
+	return nil
+}
diff --git a/vendor/github.com/danieljoos/wincred/go.mod b/vendor/github.com/danieljoos/wincred/go.mod
new file mode 100644
index 000000000..b56158042
--- /dev/null
+++ b/vendor/github.com/danieljoos/wincred/go.mod
@@ -0,0 +1,5 @@
+module github.com/danieljoos/wincred
+
+go 1.13
+
+require github.com/stretchr/testify v1.5.1
diff --git a/vendor/github.com/danieljoos/wincred/go.sum b/vendor/github.com/danieljoos/wincred/go.sum
new file mode 100644
index 000000000..c0565d71d
--- /dev/null
+++ b/vendor/github.com/danieljoos/wincred/go.sum
@@ -0,0 +1,12 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/vendor/github.com/danieljoos/wincred/sys.go b/vendor/github.com/danieljoos/wincred/sys.go
new file mode 100644
index 000000000..7b83e8438
--- /dev/null
+++ b/vendor/github.com/danieljoos/wincred/sys.go
@@ -0,0 +1,143 @@
+// +build windows
+
+package wincred
+
+import (
+	"reflect"
+	"syscall"
+	"unsafe"
+)
+
+var (
+	modadvapi32 = syscall.NewLazyDLL("advapi32.dll")
+
+	procCredRead      proc = modadvapi32.NewProc("CredReadW")
+	procCredWrite     proc = modadvapi32.NewProc("CredWriteW")
+	procCredDelete    proc = modadvapi32.NewProc("CredDeleteW")
+	procCredFree      proc = modadvapi32.NewProc("CredFree")
+	procCredEnumerate proc = modadvapi32.NewProc("CredEnumerateW")
+)
+
+// Interface for syscall.Proc: helps testing
+type proc interface {
+	Call(a ...uintptr) (r1, r2 uintptr, lastErr error)
+}
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw
+type sysCREDENTIAL struct {
+	Flags              uint32
+	Type               uint32
+	TargetName         *uint16
+	Comment            *uint16
+	LastWritten        syscall.Filetime
+	CredentialBlobSize uint32
+	CredentialBlob     uintptr
+	Persist            uint32
+	AttributeCount     uint32
+	Attributes         uintptr
+	TargetAlias        *uint16
+	UserName           *uint16
+}
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credential_attributew
+type sysCREDENTIAL_ATTRIBUTE struct {
+	Keyword   *uint16
+	Flags     uint32
+	ValueSize uint32
+	Value     uintptr
+}
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw
+type sysCRED_TYPE uint32
+
+const (
+	sysCRED_TYPE_GENERIC                 sysCRED_TYPE = 0x1
+	sysCRED_TYPE_DOMAIN_PASSWORD         sysCRED_TYPE = 0x2
+	sysCRED_TYPE_DOMAIN_CERTIFICATE      sysCRED_TYPE = 0x3
+	sysCRED_TYPE_DOMAIN_VISIBLE_PASSWORD sysCRED_TYPE = 0x4
+	sysCRED_TYPE_GENERIC_CERTIFICATE     sysCRED_TYPE = 0x5
+	sysCRED_TYPE_DOMAIN_EXTENDED         sysCRED_TYPE = 0x6
+
+	// https://docs.microsoft.com/en-us/windows/desktop/Debug/system-error-codes
+	sysERROR_NOT_FOUND         = syscall.Errno(1168)
+	sysERROR_INVALID_PARAMETER = syscall.Errno(87)
+)
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credreadw
+func sysCredRead(targetName string, typ sysCRED_TYPE) (*Credential, error) {
+	var pcred *sysCREDENTIAL
+	targetNamePtr, _ := syscall.UTF16PtrFromString(targetName)
+	ret, _, err := procCredRead.Call(
+		uintptr(unsafe.Pointer(targetNamePtr)),
+		uintptr(typ),
+		0,
+		uintptr(unsafe.Pointer(&pcred)),
+	)
+	if ret == 0 {
+		return nil, err
+	}
+	defer procCredFree.Call(uintptr(unsafe.Pointer(pcred)))
+
+	return sysToCredential(pcred), nil
+}
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credwritew
+func sysCredWrite(cred *Credential, typ sysCRED_TYPE) error {
+	ncred := sysFromCredential(cred)
+	ncred.Type = uint32(typ)
+	ret, _, err := procCredWrite.Call(
+		uintptr(unsafe.Pointer(ncred)),
+		0,
+	)
+	if ret == 0 {
+		return err
+	}
+
+	return nil
+}
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-creddeletew
+func sysCredDelete(cred *Credential, typ sysCRED_TYPE) error {
+	targetNamePtr, _ := syscall.UTF16PtrFromString(cred.TargetName)
+	ret, _, err := procCredDelete.Call(
+		uintptr(unsafe.Pointer(targetNamePtr)),
+		uintptr(typ),
+		0,
+	)
+	if ret == 0 {
+		return err
+	}
+
+	return nil
+}
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credenumeratew
+func sysCredEnumerate(filter string, all bool) ([]*Credential, error) {
+	var count int
+	var pcreds uintptr
+	var filterPtr *uint16
+	if !all {
+		filterPtr, _ = syscall.UTF16PtrFromString(filter)
+	}
+	ret, _, err := procCredEnumerate.Call(
+		uintptr(unsafe.Pointer(filterPtr)),
+		0,
+		uintptr(unsafe.Pointer(&count)),
+		uintptr(unsafe.Pointer(&pcreds)),
+	)
+	if ret == 0 {
+		return nil, err
+	}
+	defer procCredFree.Call(pcreds)
+	credsSlice := *(*[]*sysCREDENTIAL)(unsafe.Pointer(&reflect.SliceHeader{
+		Data: pcreds,
+		Len:  count,
+		Cap:  count,
+	}))
+	creds := make([]*Credential, count, count)
+	for i, cred := range credsSlice {
+		creds[i] = sysToCredential(cred)
+	}
+
+	return creds, nil
+}
diff --git a/vendor/github.com/danieljoos/wincred/sys_unsupported.go b/vendor/github.com/danieljoos/wincred/sys_unsupported.go
new file mode 100644
index 000000000..b47bccf8a
--- /dev/null
+++ b/vendor/github.com/danieljoos/wincred/sys_unsupported.go
@@ -0,0 +1,36 @@
+// +build !windows
+
+package wincred
+
+import (
+	"errors"
+	"syscall"
+)
+
+const (
+	sysCRED_TYPE_GENERIC                 = 0
+	sysCRED_TYPE_DOMAIN_PASSWORD         = 0
+	sysCRED_TYPE_DOMAIN_CERTIFICATE      = 0
+	sysCRED_TYPE_DOMAIN_VISIBLE_PASSWORD = 0
+	sysCRED_TYPE_GENERIC_CERTIFICATE     = 0
+	sysCRED_TYPE_DOMAIN_EXTENDED         = 0
+
+	sysERROR_NOT_FOUND         = syscall.Errno(1)
+	sysERROR_INVALID_PARAMETER = syscall.Errno(1)
+)
+
+func sysCredRead(...interface{}) (*Credential, error) {
+	return nil, errors.New("Operation not supported")
+}
+
+func sysCredWrite(...interface{}) error {
+	return errors.New("Operation not supported")
+}
+
+func sysCredDelete(...interface{}) error {
+	return errors.New("Operation not supported")
+}
+
+func sysCredEnumerate(...interface{}) ([]*Credential, error) {
+	return nil, errors.New("Operation not supported")
+}
diff --git a/vendor/github.com/danieljoos/wincred/types.go b/vendor/github.com/danieljoos/wincred/types.go
new file mode 100644
index 000000000..28debc932
--- /dev/null
+++ b/vendor/github.com/danieljoos/wincred/types.go
@@ -0,0 +1,69 @@
+package wincred
+
+import (
+	"time"
+)
+
+// CredentialPersistence describes one of three persistence modes of a credential.
+// A detailed description of the available modes can be found on
+// Docs: https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw
+type CredentialPersistence uint32
+
+const (
+	// PersistSession indicates that the credential only persists for the life
+	// of the current Windows login session. Such a credential is not visible in
+	// any other logon session, even from the same user.
+	PersistSession CredentialPersistence = 0x1
+
+	// PersistLocalMachine indicates that the credential persists for this and
+	// all subsequent logon sessions on this local machine/computer. It is
+	// however not visible for logon sessions of this user on a different
+	// machine.
+	PersistLocalMachine CredentialPersistence = 0x2
+
+	// PersistEnterprise indicates that the credential persists for this and all
+	// subsequent logon sessions for this user. It is also visible for logon
+	// sessions on different computers.
+	PersistEnterprise CredentialPersistence = 0x3
+)
+
+// CredentialAttribute represents an application-specific attribute of a credential.
+type CredentialAttribute struct {
+	Keyword string
+	Value   []byte
+}
+
+// Credential is the basic credential structure.
+// A credential is identified by its target name.
+// The actual credential secret is available in the CredentialBlob field.
+type Credential struct {
+	TargetName     string
+	Comment        string
+	LastWritten    time.Time
+	CredentialBlob []byte
+	Attributes     []CredentialAttribute
+	TargetAlias    string
+	UserName       string
+	Persist        CredentialPersistence
+}
+
+// GenericCredential holds a credential for generic usage.
+// It is typically defined and used by applications that need to manage user
+// secrets.
+//
+// More information about the available kinds of credentials of the Windows
+// Credential Management API can be found on Docs:
+// https://docs.microsoft.com/en-us/windows/desktop/SecAuthN/kinds-of-credentials
+type GenericCredential struct {
+	Credential
+}
+
+// DomainPassword holds a domain credential that is typically used by the
+// operating system for user logon.
+//
+// More information about the available kinds of credentials of the Windows
+// Credential Management API can be found on Docs:
+// https://docs.microsoft.com/en-us/windows/desktop/SecAuthN/kinds-of-credentials
+type DomainPassword struct {
+	Credential
+}
diff --git a/vendor/github.com/danieljoos/wincred/wincred.go b/vendor/github.com/danieljoos/wincred/wincred.go
new file mode 100644
index 000000000..c5dbc2c97
--- /dev/null
+++ b/vendor/github.com/danieljoos/wincred/wincred.go
@@ -0,0 +1,111 @@
+// Package wincred provides primitives for accessing the Windows Credentials Management API.
+// This includes functions for retrieval, listing and storage of credentials as well as Go structures for convenient access to the credential data.
+//
+// A more detailed description of Windows Credentials Management can be found on
+// Docs: https://docs.microsoft.com/en-us/windows/desktop/SecAuthN/credentials-management
+package wincred
+
+import "errors"
+
+const (
+	// ErrElementNotFound is the error that is returned if a requested element cannot be found.
+	// This error constant can be used to check if a credential could not be found.
+	ErrElementNotFound = sysERROR_NOT_FOUND
+
+	// ErrInvalidParameter is the error that is returned for invalid parameters.
+	// This error constant can be used to check if the given function parameters were invalid.
+	// For example when trying to create a new generic credential with an empty target name.
+	ErrInvalidParameter = sysERROR_INVALID_PARAMETER
+)
+
+// GetGenericCredential fetches the generic credential with the given name from Windows credential manager.
+// It returns nil and an error if the credential could not be found or an error occurred.
+func GetGenericCredential(targetName string) (*GenericCredential, error) {
+	cred, err := sysCredRead(targetName, sysCRED_TYPE_GENERIC)
+	if cred != nil {
+		return &GenericCredential{*cred}, err
+	}
+	return nil, err
+}
+
+// NewGenericCredential creates a new generic credential object with the given name.
+// The persist mode of the newly created object is set to a default value that indicates local-machine-wide storage.
+// The credential object is NOT yet persisted to the Windows credential vault.
+func NewGenericCredential(targetName string) (result *GenericCredential) {
+	result = new(GenericCredential)
+	result.TargetName = targetName
+	result.Persist = PersistLocalMachine
+	return
+}
+
+// Write persists the generic credential object to Windows credential manager.
+func (t *GenericCredential) Write() (err error) {
+	err = sysCredWrite(&t.Credential, sysCRED_TYPE_GENERIC)
+	return
+}
+
+// Delete removes the credential object from Windows credential manager.
+func (t *GenericCredential) Delete() (err error) {
+	err = sysCredDelete(&t.Credential, sysCRED_TYPE_GENERIC)
+	return
+}
+
+// GetDomainPassword fetches the domain-password credential with the given target host name from Windows credential manager.
+// It returns nil and an error if the credential could not be found or an error occurred.
+func GetDomainPassword(targetName string) (*DomainPassword, error) {
+	cred, err := sysCredRead(targetName, sysCRED_TYPE_DOMAIN_PASSWORD)
+	if cred != nil {
+		return &DomainPassword{*cred}, err
+	}
+	return nil, err
+}
+
+// NewDomainPassword creates a new domain-password credential used for login to the given target host name.
+// The  persist mode of the newly created object is set to a default value that indicates local-machine-wide storage.
+// The credential object is NOT yet persisted to the Windows credential vault.
+func NewDomainPassword(targetName string) (result *DomainPassword) {
+	result = new(DomainPassword)
+	result.TargetName = targetName
+	result.Persist = PersistLocalMachine
+	return
+}
+
+// Write persists the domain-password credential to Windows credential manager.
+func (t *DomainPassword) Write() (err error) {
+	err = sysCredWrite(&t.Credential, sysCRED_TYPE_DOMAIN_PASSWORD)
+	return
+}
+
+// Delete removes the domain-password credential from Windows credential manager.
+func (t *DomainPassword) Delete() (err error) {
+	err = sysCredDelete(&t.Credential, sysCRED_TYPE_DOMAIN_PASSWORD)
+	return
+}
+
+// SetPassword sets the CredentialBlob field of a domain password credential to the given string.
+func (t *DomainPassword) SetPassword(pw string) {
+	t.CredentialBlob = utf16ToByte(utf16FromString(pw))
+}
+
+// List retrieves all credentials of the Credentials store.
+func List() ([]*Credential, error) {
+	creds, err := sysCredEnumerate("", true)
+	if err != nil && errors.Is(err, ErrElementNotFound) {
+		// Ignore ERROR_NOT_FOUND and return an empty list instead
+		creds = []*Credential{}
+		err = nil
+	}
+	return creds, err
+}
+
+// FilteredList retrieves the list of credentials from the Credentials store that match the given filter.
+// The filter string defines the prefix followed by an asterisk for the `TargetName` attribute of the credentials.
+func FilteredList(filter string) ([]*Credential, error) {
+	creds, err := sysCredEnumerate(filter, false)
+	if err != nil && errors.Is(err, ErrElementNotFound) {
+		// Ignore ERROR_NOT_FOUND and return an empty list instead
+		creds = []*Credential{}
+		err = nil
+	}
+	return creds, err
+}
diff --git a/vendor/github.com/godbus/dbus/v5/.travis.yml b/vendor/github.com/godbus/dbus/v5/.travis.yml
new file mode 100644
index 000000000..dd6767204
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/.travis.yml
@@ -0,0 +1,50 @@
+dist: bionic
+language: go
+go_import_path: github.com/godbus/dbus
+
+go:
+  - 1.11.x
+  - 1.12.x
+  - 1.13.x
+  - tip
+
+matrix:
+  fast_finish: true
+  allow_failures:
+    - go: tip
+
+addons:
+  apt:
+    packages:
+    - dbus
+    - dbus-x11
+
+before_install:
+  - export GO111MODULE=on
+
+script:
+  - go test -v -race -mod=readonly ./...     # Run all the tests with the race detector enabled
+  - go vet ./...                             # go vet is the official Go static analyzer
+
+jobs:
+  include:
+    # The build matrix doesn't cover build stages, so manually expand
+    # the jobs with anchors
+    - &multiarch
+      stage: "Multiarch Test"
+      go: 1.11.x
+      env: TARGETS="386 arm arm64 ppc64le"
+      before_install:
+        - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
+      script:
+        - |
+          set -e
+          for target in $TARGETS; do
+            printf "\e[1mRunning test suite under ${target}.\e[0m\n"
+            GOARCH="$target" go test -v ./...
+            printf "\n\n"
+          done
+    - <<: *multiarch
+      go: 1.12.x
+    - <<: *multiarch
+      go: 1.13.x
diff --git a/vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md b/vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md
new file mode 100644
index 000000000..c88f9b2bd
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md
@@ -0,0 +1,50 @@
+# How to Contribute
+
+## Getting Started
+
+- Fork the repository on GitHub
+- Read the [README](README.markdown) for build and test instructions
+- Play with the project, submit bugs, submit patches!
+
+## Contribution Flow
+
+This is a rough outline of what a contributor's workflow looks like:
+
+- Create a topic branch from where you want to base your work (usually master).
+- Make commits of logical units.
+- Make sure your commit messages are in the proper format (see below).
+- Push your changes to a topic branch in your fork of the repository.
+- Make sure the tests pass, and add any new tests as appropriate.
+- Submit a pull request to the original repository.
+
+Thanks for your contributions!
+
+### Format of the Commit Message
+
+We follow a rough convention for commit messages that is designed to answer two
+questions: what changed and why. The subject line should feature the what and
+the body of the commit should describe the why.
+
+```
+scripts: add the test-cluster command
+
+this uses tmux to setup a test cluster that you can easily kill and
+start for debugging.
+
+Fixes #38
+```
+
+The format can be described more formally as follows:
+
+```
+<subsystem>: <what changed>
+<BLANK LINE>
+<why this change was made>
+<BLANK LINE>
+<footer>
+```
+
+The first line is the subject and should be no longer than 70 characters, the
+second line is always blank, and other lines should be wrapped at 80 characters.
+This allows the message to be easier to read on GitHub as well as in various
+git tools.
diff --git a/vendor/github.com/godbus/dbus/v5/LICENSE b/vendor/github.com/godbus/dbus/v5/LICENSE
new file mode 100644
index 000000000..670d88fca
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2013, Georg Reinke (<guelfey at gmail dot com>), Google
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. 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.
+
+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.
diff --git a/vendor/github.com/godbus/dbus/v5/MAINTAINERS b/vendor/github.com/godbus/dbus/v5/MAINTAINERS
new file mode 100644
index 000000000..27618c9cd
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/MAINTAINERS
@@ -0,0 +1,3 @@
+Brandon Philips <brandon@ifup.org> (@philips)
+Brian Waldon <brian@waldon.cc> (@bcwaldon)
+John Southworth <jsouthwo@brocade.com> (@jsouthworth)
diff --git a/vendor/github.com/godbus/dbus/v5/README.markdown b/vendor/github.com/godbus/dbus/v5/README.markdown
new file mode 100644
index 000000000..fd2964875
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/README.markdown
@@ -0,0 +1,44 @@
+[![Build Status](https://travis-ci.org/godbus/dbus.svg?branch=master)](https://travis-ci.org/godbus/dbus)
+
+dbus
+----
+
+dbus is a simple library that implements native Go client bindings for the
+D-Bus message bus system.
+
+### Features
+
+* Complete native implementation of the D-Bus message protocol
+* Go-like API (channels for signals / asynchronous method calls, Goroutine-safe connections)
+* Subpackages that help with the introspection / property interfaces
+
+### Installation
+
+This packages requires Go 1.7. If you installed it and set up your GOPATH, just run:
+
+```
+go get github.com/godbus/dbus
+```
+
+If you want to use the subpackages, you can install them the same way.
+
+### Usage
+
+The complete package documentation and some simple examples are available at
+[godoc.org](http://godoc.org/github.com/godbus/dbus). Also, the
+[_examples](https://github.com/godbus/dbus/tree/master/_examples) directory
+gives a short overview over the basic usage. 
+
+#### Projects using godbus
+- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library.
+- [go-bluetooth](https://github.com/muka/go-bluetooth) provides a bluetooth client over bluez dbus API.
+
+Please note that the API is considered unstable for now and may change without
+further notice.
+
+### License
+
+go.dbus is available under the Simplified BSD License; see LICENSE for the full
+text.
+
+Nearly all of the credit for this library goes to github.com/guelfey/go.dbus.
diff --git a/vendor/github.com/godbus/dbus/v5/auth.go b/vendor/github.com/godbus/dbus/v5/auth.go
new file mode 100644
index 000000000..31abac629
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/auth.go
@@ -0,0 +1,252 @@
+package dbus
+
+import (
+	"bufio"
+	"bytes"
+	"errors"
+	"io"
+	"os"
+	"strconv"
+)
+
+// AuthStatus represents the Status of an authentication mechanism.
+type AuthStatus byte
+
+const (
+	// AuthOk signals that authentication is finished; the next command
+	// from the server should be an OK.
+	AuthOk AuthStatus = iota
+
+	// AuthContinue signals that additional data is needed; the next command
+	// from the server should be a DATA.
+	AuthContinue
+
+	// AuthError signals an error; the server sent invalid data or some
+	// other unexpected thing happened and the current authentication
+	// process should be aborted.
+	AuthError
+)
+
+type authState byte
+
+const (
+	waitingForData authState = iota
+	waitingForOk
+	waitingForReject
+)
+
+// Auth defines the behaviour of an authentication mechanism.
+type Auth interface {
+	// Return the name of the mechnism, the argument to the first AUTH command
+	// and the next status.
+	FirstData() (name, resp []byte, status AuthStatus)
+
+	// Process the given DATA command, and return the argument to the DATA
+	// command and the next status. If len(resp) == 0, no DATA command is sent.
+	HandleData(data []byte) (resp []byte, status AuthStatus)
+}
+
+// Auth authenticates the connection, trying the given list of authentication
+// mechanisms (in that order). If nil is passed, the EXTERNAL and
+// DBUS_COOKIE_SHA1 mechanisms are tried for the current user. For private
+// connections, this method must be called before sending any messages to the
+// bus. Auth must not be called on shared connections.
+func (conn *Conn) Auth(methods []Auth) error {
+	if methods == nil {
+		uid := strconv.Itoa(os.Getuid())
+		methods = []Auth{AuthExternal(uid), AuthCookieSha1(uid, getHomeDir())}
+	}
+	in := bufio.NewReader(conn.transport)
+	err := conn.transport.SendNullByte()
+	if err != nil {
+		return err
+	}
+	err = authWriteLine(conn.transport, []byte("AUTH"))
+	if err != nil {
+		return err
+	}
+	s, err := authReadLine(in)
+	if err != nil {
+		return err
+	}
+	if len(s) < 2 || !bytes.Equal(s[0], []byte("REJECTED")) {
+		return errors.New("dbus: authentication protocol error")
+	}
+	s = s[1:]
+	for _, v := range s {
+		for _, m := range methods {
+			if name, data, status := m.FirstData(); bytes.Equal(v, name) {
+				var ok bool
+				err = authWriteLine(conn.transport, []byte("AUTH"), v, data)
+				if err != nil {
+					return err
+				}
+				switch status {
+				case AuthOk:
+					err, ok = conn.tryAuth(m, waitingForOk, in)
+				case AuthContinue:
+					err, ok = conn.tryAuth(m, waitingForData, in)
+				default:
+					panic("dbus: invalid authentication status")
+				}
+				if err != nil {
+					return err
+				}
+				if ok {
+					if conn.transport.SupportsUnixFDs() {
+						err = authWriteLine(conn, []byte("NEGOTIATE_UNIX_FD"))
+						if err != nil {
+							return err
+						}
+						line, err := authReadLine(in)
+						if err != nil {
+							return err
+						}
+						switch {
+						case bytes.Equal(line[0], []byte("AGREE_UNIX_FD")):
+							conn.EnableUnixFDs()
+							conn.unixFD = true
+						case bytes.Equal(line[0], []byte("ERROR")):
+						default:
+							return errors.New("dbus: authentication protocol error")
+						}
+					}
+					err = authWriteLine(conn.transport, []byte("BEGIN"))
+					if err != nil {
+						return err
+					}
+					go conn.inWorker()
+					return nil
+				}
+			}
+		}
+	}
+	return errors.New("dbus: authentication failed")
+}
+
+// tryAuth tries to authenticate with m as the mechanism, using state as the
+// initial authState and in for reading input. It returns (nil, true) on
+// success, (nil, false) on a REJECTED and (someErr, false) if some other
+// error occurred.
+func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) {
+	for {
+		s, err := authReadLine(in)
+		if err != nil {
+			return err, false
+		}
+		switch {
+		case state == waitingForData && string(s[0]) == "DATA":
+			if len(s) != 2 {
+				err = authWriteLine(conn.transport, []byte("ERROR"))
+				if err != nil {
+					return err, false
+				}
+				continue
+			}
+			data, status := m.HandleData(s[1])
+			switch status {
+			case AuthOk, AuthContinue:
+				if len(data) != 0 {
+					err = authWriteLine(conn.transport, []byte("DATA"), data)
+					if err != nil {
+						return err, false
+					}
+				}
+				if status == AuthOk {
+					state = waitingForOk
+				}
+			case AuthError:
+				err = authWriteLine(conn.transport, []byte("ERROR"))
+				if err != nil {
+					return err, false
+				}
+			}
+		case state == waitingForData && string(s[0]) == "REJECTED":
+			return nil, false
+		case state == waitingForData && string(s[0]) == "ERROR":
+			err = authWriteLine(conn.transport, []byte("CANCEL"))
+			if err != nil {
+				return err, false
+			}
+			state = waitingForReject
+		case state == waitingForData && string(s[0]) == "OK":
+			if len(s) != 2 {
+				err = authWriteLine(conn.transport, []byte("CANCEL"))
+				if err != nil {
+					return err, false
+				}
+				state = waitingForReject
+			}
+			conn.uuid = string(s[1])
+			return nil, true
+		case state == waitingForData:
+			err = authWriteLine(conn.transport, []byte("ERROR"))
+			if err != nil {
+				return err, false
+			}
+		case state == waitingForOk && string(s[0]) == "OK":
+			if len(s) != 2 {
+				err = authWriteLine(conn.transport, []byte("CANCEL"))
+				if err != nil {
+					return err, false
+				}
+				state = waitingForReject
+			}
+			conn.uuid = string(s[1])
+			return nil, true
+		case state == waitingForOk && string(s[0]) == "REJECTED":
+			return nil, false
+		case state == waitingForOk && (string(s[0]) == "DATA" ||
+			string(s[0]) == "ERROR"):
+
+			err = authWriteLine(conn.transport, []byte("CANCEL"))
+			if err != nil {
+				return err, false
+			}
+			state = waitingForReject
+		case state == waitingForOk:
+			err = authWriteLine(conn.transport, []byte("ERROR"))
+			if err != nil {
+				return err, false
+			}
+		case state == waitingForReject && string(s[0]) == "REJECTED":
+			return nil, false
+		case state == waitingForReject:
+			return errors.New("dbus: authentication protocol error"), false
+		default:
+			panic("dbus: invalid auth state")
+		}
+	}
+}
+
+// authReadLine reads a line and separates it into its fields.
+func authReadLine(in *bufio.Reader) ([][]byte, error) {
+	data, err := in.ReadBytes('\n')
+	if err != nil {
+		return nil, err
+	}
+	data = bytes.TrimSuffix(data, []byte("\r\n"))
+	return bytes.Split(data, []byte{' '}), nil
+}
+
+// authWriteLine writes the given line in the authentication protocol format
+// (elements of data separated by a " " and terminated by "\r\n").
+func authWriteLine(out io.Writer, data ...[]byte) error {
+	buf := make([]byte, 0)
+	for i, v := range data {
+		buf = append(buf, v...)
+		if i != len(data)-1 {
+			buf = append(buf, ' ')
+		}
+	}
+	buf = append(buf, '\r')
+	buf = append(buf, '\n')
+	n, err := out.Write(buf)
+	if err != nil {
+		return err
+	}
+	if n != len(buf) {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
diff --git a/vendor/github.com/godbus/dbus/v5/auth_anonymous.go b/vendor/github.com/godbus/dbus/v5/auth_anonymous.go
new file mode 100644
index 000000000..75f3ad34d
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/auth_anonymous.go
@@ -0,0 +1,16 @@
+package dbus
+
+// AuthAnonymous returns an Auth that uses the ANONYMOUS mechanism.
+func AuthAnonymous() Auth {
+	return &authAnonymous{}
+}
+
+type authAnonymous struct{}
+
+func (a *authAnonymous) FirstData() (name, resp []byte, status AuthStatus) {
+	return []byte("ANONYMOUS"), nil, AuthOk
+}
+
+func (a *authAnonymous) HandleData(data []byte) (resp []byte, status AuthStatus) {
+	return nil, AuthError
+}
diff --git a/vendor/github.com/godbus/dbus/v5/auth_external.go b/vendor/github.com/godbus/dbus/v5/auth_external.go
new file mode 100644
index 000000000..7e376d3ef
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/auth_external.go
@@ -0,0 +1,26 @@
+package dbus
+
+import (
+	"encoding/hex"
+)
+
+// AuthExternal returns an Auth that authenticates as the given user with the
+// EXTERNAL mechanism.
+func AuthExternal(user string) Auth {
+	return authExternal{user}
+}
+
+// AuthExternal implements the EXTERNAL authentication mechanism.
+type authExternal struct {
+	user string
+}
+
+func (a authExternal) FirstData() ([]byte, []byte, AuthStatus) {
+	b := make([]byte, 2*len(a.user))
+	hex.Encode(b, []byte(a.user))
+	return []byte("EXTERNAL"), b, AuthOk
+}
+
+func (a authExternal) HandleData(b []byte) ([]byte, AuthStatus) {
+	return nil, AuthError
+}
diff --git a/vendor/github.com/godbus/dbus/v5/auth_sha1.go b/vendor/github.com/godbus/dbus/v5/auth_sha1.go
new file mode 100644
index 000000000..80286700b
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/auth_sha1.go
@@ -0,0 +1,102 @@
+package dbus
+
+import (
+	"bufio"
+	"bytes"
+	"crypto/rand"
+	"crypto/sha1"
+	"encoding/hex"
+	"os"
+)
+
+// AuthCookieSha1 returns an Auth that authenticates as the given user with the
+// DBUS_COOKIE_SHA1 mechanism. The home parameter should specify the home
+// directory of the user.
+func AuthCookieSha1(user, home string) Auth {
+	return authCookieSha1{user, home}
+}
+
+type authCookieSha1 struct {
+	user, home string
+}
+
+func (a authCookieSha1) FirstData() ([]byte, []byte, AuthStatus) {
+	b := make([]byte, 2*len(a.user))
+	hex.Encode(b, []byte(a.user))
+	return []byte("DBUS_COOKIE_SHA1"), b, AuthContinue
+}
+
+func (a authCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) {
+	challenge := make([]byte, len(data)/2)
+	_, err := hex.Decode(challenge, data)
+	if err != nil {
+		return nil, AuthError
+	}
+	b := bytes.Split(challenge, []byte{' '})
+	if len(b) != 3 {
+		return nil, AuthError
+	}
+	context := b[0]
+	id := b[1]
+	svchallenge := b[2]
+	cookie := a.getCookie(context, id)
+	if cookie == nil {
+		return nil, AuthError
+	}
+	clchallenge := a.generateChallenge()
+	if clchallenge == nil {
+		return nil, AuthError
+	}
+	hash := sha1.New()
+	hash.Write(bytes.Join([][]byte{svchallenge, clchallenge, cookie}, []byte{':'}))
+	hexhash := make([]byte, 2*hash.Size())
+	hex.Encode(hexhash, hash.Sum(nil))
+	data = append(clchallenge, ' ')
+	data = append(data, hexhash...)
+	resp := make([]byte, 2*len(data))
+	hex.Encode(resp, data)
+	return resp, AuthOk
+}
+
+// getCookie searches for the cookie identified by id in context and returns
+// the cookie content or nil. (Since HandleData can't return a specific error,
+// but only whether an error occurred, this function also doesn't bother to
+// return an error.)
+func (a authCookieSha1) getCookie(context, id []byte) []byte {
+	file, err := os.Open(a.home + "/.dbus-keyrings/" + string(context))
+	if err != nil {
+		return nil
+	}
+	defer file.Close()
+	rd := bufio.NewReader(file)
+	for {
+		line, err := rd.ReadBytes('\n')
+		if err != nil {
+			return nil
+		}
+		line = line[:len(line)-1]
+		b := bytes.Split(line, []byte{' '})
+		if len(b) != 3 {
+			return nil
+		}
+		if bytes.Equal(b[0], id) {
+			return b[2]
+		}
+	}
+}
+
+// generateChallenge returns a random, hex-encoded challenge, or nil on error
+// (see above).
+func (a authCookieSha1) generateChallenge() []byte {
+	b := make([]byte, 16)
+	n, err := rand.Read(b)
+	if err != nil {
+		return nil
+	}
+	if n != 16 {
+		return nil
+	}
+	enc := make([]byte, 32)
+	hex.Encode(enc, b)
+	return enc
+}
diff --git a/vendor/github.com/godbus/dbus/v5/call.go b/vendor/github.com/godbus/dbus/v5/call.go
new file mode 100644
index 000000000..2cb189012
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/call.go
@@ -0,0 +1,60 @@
+package dbus
+
+import (
+	"context"
+	"errors"
+)
+
+var errSignature = errors.New("dbus: mismatched signature")
+
+// Call represents a pending or completed method call.
+type Call struct {
+	Destination string
+	Path        ObjectPath
+	Method      string
+	Args        []interface{}
+
+	// Strobes when the call is complete.
+	Done chan *Call
+
+	// After completion, the error status. If this is non-nil, it may be an
+	// error message from the peer (with Error as its type) or some other error.
+	Err error
+
+	// Holds the response once the call is done.
+	Body []interface{}
+
+	// tracks context and canceler
+	ctx         context.Context
+	ctxCanceler context.CancelFunc
+}
+
+func (c *Call) Context() context.Context {
+	if c.ctx == nil {
+		return context.Background()
+	}
+
+	return c.ctx
+}
+
+func (c *Call) ContextCancel() {
+	if c.ctxCanceler != nil {
+		c.ctxCanceler()
+	}
+}
+
+// Store stores the body of the reply into the provided pointers. It returns
+// an error if the signatures of the body and retvalues don't match, or if
+// the error status is not nil.
+func (c *Call) Store(retvalues ...interface{}) error {
+	if c.Err != nil {
+		return c.Err
+	}
+
+	return Store(c.Body, retvalues...)
+}
+
+func (c *Call) done() {
+	c.Done <- c
+	c.ContextCancel()
+}
diff --git a/vendor/github.com/godbus/dbus/v5/conn.go b/vendor/github.com/godbus/dbus/v5/conn.go
new file mode 100644
index 000000000..b55bc99c8
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/conn.go
@@ -0,0 +1,912 @@
+package dbus
+
+import (
+	"context"
+	"errors"
+	"io"
+	"os"
+	"strings"
+	"sync"
+)
+
+var (
+	systemBus     *Conn
+	systemBusLck  sync.Mutex
+	sessionBus    *Conn
+	sessionBusLck sync.Mutex
+)
+
+// ErrClosed is the error returned by calls on a closed connection.
+var ErrClosed = errors.New("dbus: connection closed by user")
+
+// Conn represents a connection to a message bus (usually, the system or
+// session bus).
+//
+// Connections are either shared or private. Shared connections
+// are shared between calls to the functions that return them. As a result,
+// the methods Close, Auth and Hello must not be called on them.
+//
+// Multiple goroutines may invoke methods on a connection simultaneously.
+type Conn struct {
+	transport
+
+	ctx       context.Context
+	cancelCtx context.CancelFunc
+
+	closeOnce sync.Once
+	closeErr  error
+
+	busObj BusObject
+	unixFD bool
+	uuid   string
+
+	handler       Handler
+	signalHandler SignalHandler
+	serialGen     SerialGenerator
+	inInt         Interceptor
+	outInt        Interceptor
+
+	names      *nameTracker
+	calls      *callTracker
+	outHandler *outputHandler
+
+	eavesdropped    chan<- *Message
+	eavesdroppedLck sync.Mutex
+}
+
+// SessionBus returns a shared connection to the session bus, connecting to it
+// if not already done.
+func SessionBus() (conn *Conn, err error) {
+	sessionBusLck.Lock()
+	defer sessionBusLck.Unlock()
+	if sessionBus != nil {
+		return sessionBus, nil
+	}
+	defer func() {
+		if conn != nil {
+			sessionBus = conn
+		}
+	}()
+	conn, err = SessionBusPrivate()
+	if err != nil {
+		return
+	}
+	if err = conn.Auth(nil); err != nil {
+		conn.Close()
+		conn = nil
+		return
+	}
+	if err = conn.Hello(); err != nil {
+		conn.Close()
+		conn = nil
+	}
+	return
+}
+
+func getSessionBusAddress() (string, error) {
+	if address := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); address != "" && address != "autolaunch:" {
+		return address, nil
+
+	} else if address := tryDiscoverDbusSessionBusAddress(); address != "" {
+		os.Setenv("DBUS_SESSION_BUS_ADDRESS", address)
+		return address, nil
+	}
+	return getSessionBusPlatformAddress()
+}
+
+// SessionBusPrivate returns a new private connection to the session bus.
+func SessionBusPrivate(opts ...ConnOption) (*Conn, error) {
+	address, err := getSessionBusAddress()
+	if err != nil {
+		return nil, err
+	}
+
+	return Dial(address, opts...)
+}
+
+// SessionBusPrivate returns a new private connection to the session bus.
+//
+// Deprecated: use SessionBusPrivate with options instead.
+func SessionBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) {
+	return SessionBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler))
+}
+
+// SystemBus returns a shared connection to the system bus, connecting to it if
+// not already done.
+func SystemBus() (conn *Conn, err error) {
+	systemBusLck.Lock()
+	defer systemBusLck.Unlock()
+	if systemBus != nil {
+		return systemBus, nil
+	}
+	defer func() {
+		if conn != nil {
+			systemBus = conn
+		}
+	}()
+	conn, err = SystemBusPrivate()
+	if err != nil {
+		return
+	}
+	if err = conn.Auth(nil); err != nil {
+		conn.Close()
+		conn = nil
+		return
+	}
+	if err = conn.Hello(); err != nil {
+		conn.Close()
+		conn = nil
+	}
+	return
+}
+
+// SystemBusPrivate returns a new private connection to the system bus.
+// Note: this connection is not ready to use. One must perform Auth and Hello
+// on the connection before it is useable.
+func SystemBusPrivate(opts ...ConnOption) (*Conn, error) {
+	return Dial(getSystemBusPlatformAddress(), opts...)
+}
+
+// SystemBusPrivateHandler returns a new private connection to the system bus, using the provided handlers.
+//
+// Deprecated: use SystemBusPrivate with options instead.
+func SystemBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) {
+	return SystemBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler))
+}
+
+// Dial establishes a new private connection to the message bus specified by address.
+func Dial(address string, opts ...ConnOption) (*Conn, error) {
+	tr, err := getTransport(address)
+	if err != nil {
+		return nil, err
+	}
+	return newConn(tr, opts...)
+}
+
+// DialHandler establishes a new private connection to the message bus specified by address, using the supplied handlers.
+//
+// Deprecated: use Dial with options instead.
+func DialHandler(address string, handler Handler, signalHandler SignalHandler) (*Conn, error) {
+	return Dial(address, WithSignalHandler(signalHandler))
+}
+
+// ConnOption is a connection option.
+type ConnOption func(conn *Conn) error
+
+// WithHandler overrides the default handler.
+func WithHandler(handler Handler) ConnOption {
+	return func(conn *Conn) error {
+		conn.handler = handler
+		return nil
+	}
+}
+
+// WithSignalHandler overrides the default signal handler.
+func WithSignalHandler(handler SignalHandler) ConnOption {
+	return func(conn *Conn) error {
+		conn.signalHandler = handler
+		return nil
+	}
+}
+
+// WithSerialGenerator overrides the default signals generator.
+func WithSerialGenerator(gen SerialGenerator) ConnOption {
+	return func(conn *Conn) error {
+		conn.serialGen = gen
+		return nil
+	}
+}
+
+// Interceptor intercepts incoming and outgoing messages.
+type Interceptor func(msg *Message)
+
+// WithIncomingInterceptor sets the given interceptor for incoming messages.
+func WithIncomingInterceptor(interceptor Interceptor) ConnOption {
+	return func(conn *Conn) error {
+		conn.inInt = interceptor
+		return nil
+	}
+}
+
+// WithOutgoingInterceptor sets the given interceptor for outgoing messages.
+func WithOutgoingInterceptor(interceptor Interceptor) ConnOption {
+	return func(conn *Conn) error {
+		conn.outInt = interceptor
+		return nil
+	}
+}
+
+// WithContext overrides  the default context for the connection.
+func WithContext(ctx context.Context) ConnOption {
+	return func(conn *Conn) error {
+		conn.ctx = ctx
+		return nil
+	}
+}
+
+// NewConn creates a new private *Conn from an already established connection.
+func NewConn(conn io.ReadWriteCloser, opts ...ConnOption) (*Conn, error) {
+	return newConn(genericTransport{conn}, opts...)
+}
+
+// NewConnHandler creates a new private *Conn from an already established connection, using the supplied handlers.
+//
+// Deprecated: use NewConn with options instead.
+func NewConnHandler(conn io.ReadWriteCloser, handler Handler, signalHandler SignalHandler) (*Conn, error) {
+	return NewConn(genericTransport{conn}, WithHandler(handler), WithSignalHandler(signalHandler))
+}
+
+// newConn creates a new *Conn from a transport.
+func newConn(tr transport, opts ...ConnOption) (*Conn, error) {
+	conn := new(Conn)
+	conn.transport = tr
+	for _, opt := range opts {
+		if err := opt(conn); err != nil {
+			return nil, err
+		}
+	}
+	if conn.ctx == nil {
+		conn.ctx = context.Background()
+	}
+	conn.ctx, conn.cancelCtx = context.WithCancel(conn.ctx)
+	go func() {
+		<-conn.ctx.Done()
+		conn.Close()
+	}()
+
+	conn.calls = newCallTracker()
+	if conn.handler == nil {
+		conn.handler = NewDefaultHandler()
+	}
+	if conn.signalHandler == nil {
+		conn.signalHandler = NewDefaultSignalHandler()
+	}
+	if conn.serialGen == nil {
+		conn.serialGen = newSerialGenerator()
+	}
+	conn.outHandler = &outputHandler{conn: conn}
+	conn.names = newNameTracker()
+	conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
+	return conn, nil
+}
+
+// BusObject returns the object owned by the bus daemon which handles
+// administrative requests.
+func (conn *Conn) BusObject() BusObject {
+	return conn.busObj
+}
+
+// Close closes the connection. Any blocked operations will return with errors
+// and the channels passed to Eavesdrop and Signal are closed. This method must
+// not be called on shared connections.
+func (conn *Conn) Close() error {
+	conn.closeOnce.Do(func() {
+		conn.outHandler.close()
+		if term, ok := conn.signalHandler.(Terminator); ok {
+			term.Terminate()
+		}
+
+		if term, ok := conn.handler.(Terminator); ok {
+			term.Terminate()
+		}
+
+		conn.eavesdroppedLck.Lock()
+		if conn.eavesdropped != nil {
+			close(conn.eavesdropped)
+		}
+		conn.eavesdroppedLck.Unlock()
+
+		conn.cancelCtx()
+
+		conn.closeErr = conn.transport.Close()
+	})
+	return conn.closeErr
+}
+
+// Context returns the context associated with the connection.  The
+// context will be cancelled when the connection is closed.
+func (conn *Conn) Context() context.Context {
+	return conn.ctx
+}
+
+// Eavesdrop causes conn to send all incoming messages to the given channel
+// without further processing. Method replies, errors and signals will not be
+// sent to the appropriate channels and method calls will not be handled. If nil
+// is passed, the normal behaviour is restored.
+//
+// The caller has to make sure that ch is sufficiently buffered;
+// if a message arrives when a write to ch is not possible, the message is
+// discarded.
+func (conn *Conn) Eavesdrop(ch chan<- *Message) {
+	conn.eavesdroppedLck.Lock()
+	conn.eavesdropped = ch
+	conn.eavesdroppedLck.Unlock()
+}
+
+// getSerial returns an unused serial.
+func (conn *Conn) getSerial() uint32 {
+	return conn.serialGen.GetSerial()
+}
+
+// Hello sends the initial org.freedesktop.DBus.Hello call. This method must be
+// called after authentication, but before sending any other messages to the
+// bus. Hello must not be called for shared connections.
+func (conn *Conn) Hello() error {
+	var s string
+	err := conn.busObj.Call("org.freedesktop.DBus.Hello", 0).Store(&s)
+	if err != nil {
+		return err
+	}
+	conn.names.acquireUniqueConnectionName(s)
+	return nil
+}
+
+// inWorker runs in an own goroutine, reading incoming messages from the
+// transport and dispatching them appropiately.
+func (conn *Conn) inWorker() {
+	for {
+		msg, err := conn.ReadMessage()
+		if err != nil {
+			if _, ok := err.(InvalidMessageError); !ok {
+				// Some read error occurred (usually EOF); we can't really do
+				// anything but to shut down all stuff and returns errors to all
+				// pending replies.
+				conn.Close()
+				conn.calls.finalizeAllWithError(err)
+				return
+			}
+			// invalid messages are ignored
+			continue
+		}
+		conn.eavesdroppedLck.Lock()
+		if conn.eavesdropped != nil {
+			select {
+			case conn.eavesdropped <- msg:
+			default:
+			}
+			conn.eavesdroppedLck.Unlock()
+			continue
+		}
+		conn.eavesdroppedLck.Unlock()
+		dest, _ := msg.Headers[FieldDestination].value.(string)
+		found := dest == "" ||
+			!conn.names.uniqueNameIsKnown() ||
+			conn.names.isKnownName(dest)
+		if !found {
+			// Eavesdropped a message, but no channel for it is registered.
+			// Ignore it.
+			continue
+		}
+
+		if conn.inInt != nil {
+			conn.inInt(msg)
+		}
+		switch msg.Type {
+		case TypeError:
+			conn.serialGen.RetireSerial(conn.calls.handleDBusError(msg))
+		case TypeMethodReply:
+			conn.serialGen.RetireSerial(conn.calls.handleReply(msg))
+		case TypeSignal:
+			conn.handleSignal(msg)
+		case TypeMethodCall:
+			go conn.handleCall(msg)
+		}
+
+	}
+}
+
+func (conn *Conn) handleSignal(msg *Message) {
+	iface := msg.Headers[FieldInterface].value.(string)
+	member := msg.Headers[FieldMember].value.(string)
+	// as per http://dbus.freedesktop.org/doc/dbus-specification.html ,
+	// sender is optional for signals.
+	sender, _ := msg.Headers[FieldSender].value.(string)
+	if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" {
+		if member == "NameLost" {
+			// If we lost the name on the bus, remove it from our
+			// tracking list.
+			name, ok := msg.Body[0].(string)
+			if !ok {
+				panic("Unable to read the lost name")
+			}
+			conn.names.loseName(name)
+		} else if member == "NameAcquired" {
+			// If we acquired the name on the bus, add it to our
+			// tracking list.
+			name, ok := msg.Body[0].(string)
+			if !ok {
+				panic("Unable to read the acquired name")
+			}
+			conn.names.acquireName(name)
+		}
+	}
+	signal := &Signal{
+		Sender: sender,
+		Path:   msg.Headers[FieldPath].value.(ObjectPath),
+		Name:   iface + "." + member,
+		Body:   msg.Body,
+	}
+	conn.signalHandler.DeliverSignal(iface, member, signal)
+}
+
+// Names returns the list of all names that are currently owned by this
+// connection. The slice is always at least one element long, the first element
+// being the unique name of the connection.
+func (conn *Conn) Names() []string {
+	return conn.names.listKnownNames()
+}
+
+// Object returns the object identified by the given destination name and path.
+func (conn *Conn) Object(dest string, path ObjectPath) BusObject {
+	return &Object{conn, dest, path}
+}
+
+func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) {
+	if conn.outInt != nil {
+		conn.outInt(msg)
+	}
+	err := conn.outHandler.sendAndIfClosed(msg, ifClosed)
+	conn.calls.handleSendError(msg, err)
+	if err != nil {
+		conn.serialGen.RetireSerial(msg.serial)
+	} else if msg.Type != TypeMethodCall {
+		conn.serialGen.RetireSerial(msg.serial)
+	}
+}
+
+// Send sends the given message to the message bus. You usually don't need to
+// use this; use the higher-level equivalents (Call / Go, Emit and Export)
+// instead. If msg is a method call and NoReplyExpected is not set, a non-nil
+// call is returned and the same value is sent to ch (which must be buffered)
+// once the call is complete. Otherwise, ch is ignored and a Call structure is
+// returned of which only the Err member is valid.
+func (conn *Conn) Send(msg *Message, ch chan *Call) *Call {
+	return conn.send(context.Background(), msg, ch)
+}
+
+// SendWithContext acts like Send but takes a context
+func (conn *Conn) SendWithContext(ctx context.Context, msg *Message, ch chan *Call) *Call {
+	return conn.send(ctx, msg, ch)
+}
+
+func (conn *Conn) send(ctx context.Context, msg *Message, ch chan *Call) *Call {
+	if ctx == nil {
+		panic("nil context")
+	}
+
+	var call *Call
+	ctx, canceler := context.WithCancel(ctx)
+	msg.serial = conn.getSerial()
+	if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 {
+		if ch == nil {
+			ch = make(chan *Call, 5)
+		} else if cap(ch) == 0 {
+			panic("dbus: unbuffered channel passed to (*Conn).Send")
+		}
+		call = new(Call)
+		call.Destination, _ = msg.Headers[FieldDestination].value.(string)
+		call.Path, _ = msg.Headers[FieldPath].value.(ObjectPath)
+		iface, _ := msg.Headers[FieldInterface].value.(string)
+		member, _ := msg.Headers[FieldMember].value.(string)
+		call.Method = iface + "." + member
+		call.Args = msg.Body
+		call.Done = ch
+		call.ctx = ctx
+		call.ctxCanceler = canceler
+		conn.calls.track(msg.serial, call)
+		go func() {
+			<-ctx.Done()
+			conn.calls.handleSendError(msg, ctx.Err())
+		}()
+		conn.sendMessageAndIfClosed(msg, func() {
+			conn.calls.handleSendError(msg, ErrClosed)
+			canceler()
+		})
+	} else {
+		canceler()
+		call = &Call{Err: nil}
+		conn.sendMessageAndIfClosed(msg, func() {
+			call = &Call{Err: ErrClosed}
+		})
+	}
+	return call
+}
+
+// sendError creates an error message corresponding to the parameters and sends
+// it to conn.out.
+func (conn *Conn) sendError(err error, dest string, serial uint32) {
+	var e *Error
+	switch em := err.(type) {
+	case Error:
+		e = &em
+	case *Error:
+		e = em
+	case DBusError:
+		name, body := em.DBusError()
+		e = NewError(name, body)
+	default:
+		e = MakeFailedError(err)
+	}
+	msg := new(Message)
+	msg.Type = TypeError
+	msg.serial = conn.getSerial()
+	msg.Headers = make(map[HeaderField]Variant)
+	if dest != "" {
+		msg.Headers[FieldDestination] = MakeVariant(dest)
+	}
+	msg.Headers[FieldErrorName] = MakeVariant(e.Name)
+	msg.Headers[FieldReplySerial] = MakeVariant(serial)
+	msg.Body = e.Body
+	if len(e.Body) > 0 {
+		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...))
+	}
+	conn.sendMessageAndIfClosed(msg, nil)
+}
+
+// sendReply creates a method reply message corresponding to the parameters and
+// sends it to conn.out.
+func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) {
+	msg := new(Message)
+	msg.Type = TypeMethodReply
+	msg.serial = conn.getSerial()
+	msg.Headers = make(map[HeaderField]Variant)
+	if dest != "" {
+		msg.Headers[FieldDestination] = MakeVariant(dest)
+	}
+	msg.Headers[FieldReplySerial] = MakeVariant(serial)
+	msg.Body = values
+	if len(values) > 0 {
+		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
+	}
+	conn.sendMessageAndIfClosed(msg, nil)
+}
+
+// AddMatchSignal registers the given match rule to receive broadcast
+// signals based on their contents.
+func (conn *Conn) AddMatchSignal(options ...MatchOption) error {
+	options = append([]MatchOption{withMatchType("signal")}, options...)
+	return conn.busObj.Call(
+		"org.freedesktop.DBus.AddMatch", 0,
+		formatMatchOptions(options),
+	).Store()
+}
+
+// RemoveMatchSignal removes the first rule that matches previously registered with AddMatchSignal.
+func (conn *Conn) RemoveMatchSignal(options ...MatchOption) error {
+	options = append([]MatchOption{withMatchType("signal")}, options...)
+	return conn.busObj.Call(
+		"org.freedesktop.DBus.RemoveMatch", 0,
+		formatMatchOptions(options),
+	).Store()
+}
+
+// Signal registers the given channel to be passed all received signal messages.
+//
+// Multiple of these channels can be registered at the same time.
+//
+// These channels are "overwritten" by Eavesdrop; i.e., if there currently is a
+// channel for eavesdropped messages, this channel receives all signals, and
+// none of the channels passed to Signal will receive any signals.
+//
+// Panics if the signal handler is not a `SignalRegistrar`.
+func (conn *Conn) Signal(ch chan<- *Signal) {
+	handler, ok := conn.signalHandler.(SignalRegistrar)
+	if !ok {
+		panic("cannot use this method with a non SignalRegistrar handler")
+	}
+	handler.AddSignal(ch)
+}
+
+// RemoveSignal removes the given channel from the list of the registered channels.
+//
+// Panics if the signal handler is not a `SignalRegistrar`.
+func (conn *Conn) RemoveSignal(ch chan<- *Signal) {
+	handler, ok := conn.signalHandler.(SignalRegistrar)
+	if !ok {
+		panic("cannot use this method with a non SignalRegistrar handler")
+	}
+	handler.RemoveSignal(ch)
+}
+
+// SupportsUnixFDs returns whether the underlying transport supports passing of
+// unix file descriptors. If this is false, method calls containing unix file
+// descriptors will return an error and emitted signals containing them will
+// not be sent.
+func (conn *Conn) SupportsUnixFDs() bool {
+	return conn.unixFD
+}
+
+// Error represents a D-Bus message of type Error.
+type Error struct {
+	Name string
+	Body []interface{}
+}
+
+func NewError(name string, body []interface{}) *Error {
+	return &Error{name, body}
+}
+
+func (e Error) Error() string {
+	if len(e.Body) >= 1 {
+		s, ok := e.Body[0].(string)
+		if ok {
+			return s
+		}
+	}
+	return e.Name
+}
+
+// Signal represents a D-Bus message of type Signal. The name member is given in
+// "interface.member" notation, e.g. org.freedesktop.D-Bus.NameLost.
+type Signal struct {
+	Sender string
+	Path   ObjectPath
+	Name   string
+	Body   []interface{}
+}
+
+// transport is a D-Bus transport.
+type transport interface {
+	// Read and Write raw data (for example, for the authentication protocol).
+	io.ReadWriteCloser
+
+	// Send the initial null byte used for the EXTERNAL mechanism.
+	SendNullByte() error
+
+	// Returns whether this transport supports passing Unix FDs.
+	SupportsUnixFDs() bool
+
+	// Signal the transport that Unix FD passing is enabled for this connection.
+	EnableUnixFDs()
+
+	// Read / send a message, handling things like Unix FDs.
+	ReadMessage() (*Message, error)
+	SendMessage(*Message) error
+}
+
+var (
+	transports = make(map[string]func(string) (transport, error))
+)
+
+func getTransport(address string) (transport, error) {
+	var err error
+	var t transport
+
+	addresses := strings.Split(address, ";")
+	for _, v := range addresses {
+		i := strings.IndexRune(v, ':')
+		if i == -1 {
+			err = errors.New("dbus: invalid bus address (no transport)")
+			continue
+		}
+		f := transports[v[:i]]
+		if f == nil {
+			err = errors.New("dbus: invalid bus address (invalid or unsupported transport)")
+			continue
+		}
+		t, err = f(v[i+1:])
+		if err == nil {
+			return t, nil
+		}
+	}
+	return nil, err
+}
+
+// getKey gets a key from a the list of keys. Returns "" on error / not found...
+func getKey(s, key string) string {
+	for _, keyEqualsValue := range strings.Split(s, ",") {
+		keyValue := strings.SplitN(keyEqualsValue, "=", 2)
+		if len(keyValue) == 2 && keyValue[0] == key {
+			return keyValue[1]
+		}
+	}
+	return ""
+}
+
+type outputHandler struct {
+	conn    *Conn
+	sendLck sync.Mutex
+	closed  struct {
+		isClosed bool
+		lck      sync.RWMutex
+	}
+}
+
+func (h *outputHandler) sendAndIfClosed(msg *Message, ifClosed func()) error {
+	h.closed.lck.RLock()
+	defer h.closed.lck.RUnlock()
+	if h.closed.isClosed {
+		if ifClosed != nil {
+			ifClosed()
+		}
+		return nil
+	}
+	h.sendLck.Lock()
+	defer h.sendLck.Unlock()
+	return h.conn.SendMessage(msg)
+}
+
+func (h *outputHandler) close() {
+	h.closed.lck.Lock()
+	defer h.closed.lck.Unlock()
+	h.closed.isClosed = true
+}
+
+type serialGenerator struct {
+	lck        sync.Mutex
+	nextSerial uint32
+	serialUsed map[uint32]bool
+}
+
+func newSerialGenerator() *serialGenerator {
+	return &serialGenerator{
+		serialUsed: map[uint32]bool{0: true},
+		nextSerial: 1,
+	}
+}
+
+func (gen *serialGenerator) GetSerial() uint32 {
+	gen.lck.Lock()
+	defer gen.lck.Unlock()
+	n := gen.nextSerial
+	for gen.serialUsed[n] {
+		n++
+	}
+	gen.serialUsed[n] = true
+	gen.nextSerial = n + 1
+	return n
+}
+
+func (gen *serialGenerator) RetireSerial(serial uint32) {
+	gen.lck.Lock()
+	defer gen.lck.Unlock()
+	delete(gen.serialUsed, serial)
+}
+
+type nameTracker struct {
+	lck    sync.RWMutex
+	unique string
+	names  map[string]struct{}
+}
+
+func newNameTracker() *nameTracker {
+	return &nameTracker{names: map[string]struct{}{}}
+}
+func (tracker *nameTracker) acquireUniqueConnectionName(name string) {
+	tracker.lck.Lock()
+	defer tracker.lck.Unlock()
+	tracker.unique = name
+}
+func (tracker *nameTracker) acquireName(name string) {
+	tracker.lck.Lock()
+	defer tracker.lck.Unlock()
+	tracker.names[name] = struct{}{}
+}
+func (tracker *nameTracker) loseName(name string) {
+	tracker.lck.Lock()
+	defer tracker.lck.Unlock()
+	delete(tracker.names, name)
+}
+
+func (tracker *nameTracker) uniqueNameIsKnown() bool {
+	tracker.lck.RLock()
+	defer tracker.lck.RUnlock()
+	return tracker.unique != ""
+}
+func (tracker *nameTracker) isKnownName(name string) bool {
+	tracker.lck.RLock()
+	defer tracker.lck.RUnlock()
+	_, ok := tracker.names[name]
+	return ok || name == tracker.unique
+}
+func (tracker *nameTracker) listKnownNames() []string {
+	tracker.lck.RLock()
+	defer tracker.lck.RUnlock()
+	out := make([]string, 0, len(tracker.names)+1)
+	out = append(out, tracker.unique)
+	for k := range tracker.names {
+		out = append(out, k)
+	}
+	return out
+}
+
+type callTracker struct {
+	calls map[uint32]*Call
+	lck   sync.RWMutex
+}
+
+func newCallTracker() *callTracker {
+	return &callTracker{calls: map[uint32]*Call{}}
+}
+
+func (tracker *callTracker) track(sn uint32, call *Call) {
+	tracker.lck.Lock()
+	tracker.calls[sn] = call
+	tracker.lck.Unlock()
+}
+
+func (tracker *callTracker) handleReply(msg *Message) uint32 {
+	serial := msg.Headers[FieldReplySerial].value.(uint32)
+	tracker.lck.RLock()
+	_, ok := tracker.calls[serial]
+	tracker.lck.RUnlock()
+	if ok {
+		tracker.finalizeWithBody(serial, msg.Body)
+	}
+	return serial
+}
+
+func (tracker *callTracker) handleDBusError(msg *Message) uint32 {
+	serial := msg.Headers[FieldReplySerial].value.(uint32)
+	tracker.lck.RLock()
+	_, ok := tracker.calls[serial]
+	tracker.lck.RUnlock()
+	if ok {
+		name, _ := msg.Headers[FieldErrorName].value.(string)
+		tracker.finalizeWithError(serial, Error{name, msg.Body})
+	}
+	return serial
+}
+
+func (tracker *callTracker) handleSendError(msg *Message, err error) {
+	if err == nil {
+		return
+	}
+	tracker.lck.RLock()
+	_, ok := tracker.calls[msg.serial]
+	tracker.lck.RUnlock()
+	if ok {
+		tracker.finalizeWithError(msg.serial, err)
+	}
+}
+
+// finalize was the only func that did not strobe Done
+func (tracker *callTracker) finalize(sn uint32) {
+	tracker.lck.Lock()
+	defer tracker.lck.Unlock()
+	c, ok := tracker.calls[sn]
+	if ok {
+		delete(tracker.calls, sn)
+		c.ContextCancel()
+	}
+}
+
+func (tracker *callTracker) finalizeWithBody(sn uint32, body []interface{}) {
+	tracker.lck.Lock()
+	c, ok := tracker.calls[sn]
+	if ok {
+		delete(tracker.calls, sn)
+	}
+	tracker.lck.Unlock()
+	if ok {
+		c.Body = body
+		c.done()
+	}
+}
+
+func (tracker *callTracker) finalizeWithError(sn uint32, err error) {
+	tracker.lck.Lock()
+	c, ok := tracker.calls[sn]
+	if ok {
+		delete(tracker.calls, sn)
+	}
+	tracker.lck.Unlock()
+	if ok {
+		c.Err = err
+		c.done()
+	}
+}
+
+func (tracker *callTracker) finalizeAllWithError(err error) {
+	tracker.lck.Lock()
+	closedCalls := make([]*Call, 0, len(tracker.calls))
+	for sn := range tracker.calls {
+		closedCalls = append(closedCalls, tracker.calls[sn])
+	}
+	tracker.calls = map[uint32]*Call{}
+	tracker.lck.Unlock()
+	for _, call := range closedCalls {
+		call.Err = err
+		call.done()
+	}
+}
diff --git a/vendor/github.com/godbus/dbus/v5/conn_darwin.go b/vendor/github.com/godbus/dbus/v5/conn_darwin.go
new file mode 100644
index 000000000..6e2e40202
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/conn_darwin.go
@@ -0,0 +1,37 @@
+package dbus
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"os/exec"
+)
+
+const defaultSystemBusAddress = "unix:path=/opt/local/var/run/dbus/system_bus_socket"
+
+func getSessionBusPlatformAddress() (string, error) {
+	cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET")
+	b, err := cmd.CombinedOutput()
+
+	if err != nil {
+		return "", err
+	}
+
+	if len(b) == 0 {
+		return "", errors.New("dbus: couldn't determine address of session bus")
+	}
+
+	return "unix:path=" + string(b[:len(b)-1]), nil
+}
+
+func getSystemBusPlatformAddress() string {
+	address := os.Getenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET")
+	if address != "" {
+		return fmt.Sprintf("unix:path=%s", address)
+	}
+	return defaultSystemBusAddress
+}
+
+func tryDiscoverDbusSessionBusAddress() string {
+	return ""
+}
diff --git a/vendor/github.com/godbus/dbus/v5/conn_other.go b/vendor/github.com/godbus/dbus/v5/conn_other.go
new file mode 100644
index 000000000..616dcf664
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/conn_other.go
@@ -0,0 +1,93 @@
+// +build !darwin
+
+package dbus
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"os/user"
+	"path"
+	"strings"
+)
+
+var execCommand = exec.Command
+
+func getSessionBusPlatformAddress() (string, error) {
+	cmd := execCommand("dbus-launch")
+	b, err := cmd.CombinedOutput()
+
+	if err != nil {
+		return "", err
+	}
+
+	i := bytes.IndexByte(b, '=')
+	j := bytes.IndexByte(b, '\n')
+
+	if i == -1 || j == -1 || i > j {
+		return "", errors.New("dbus: couldn't determine address of session bus")
+	}
+
+	env, addr := string(b[0:i]), string(b[i+1:j])
+	os.Setenv(env, addr)
+
+	return addr, nil
+}
+
+// tryDiscoverDbusSessionBusAddress tries to discover an existing dbus session
+// and return the value of its DBUS_SESSION_BUS_ADDRESS.
+// It tries different techniques employed by different operating systems,
+// returning the first valid address it finds, or an empty string.
+//
+// * /run/user/<uid>/bus           if this exists, it *is* the bus socket. present on
+//                                 Ubuntu 18.04
+// * /run/user/<uid>/dbus-session: if this exists, it can be parsed for the bus
+//                                 address. present on Ubuntu 16.04
+//
+// See https://dbus.freedesktop.org/doc/dbus-launch.1.html
+func tryDiscoverDbusSessionBusAddress() string {
+	if runtimeDirectory, err := getRuntimeDirectory(); err == nil {
+
+		if runUserBusFile := path.Join(runtimeDirectory, "bus"); fileExists(runUserBusFile) {
+			// if /run/user/<uid>/bus exists, that file itself
+			// *is* the unix socket, so return its path
+			return fmt.Sprintf("unix:path=%s", runUserBusFile)
+		}
+		if runUserSessionDbusFile := path.Join(runtimeDirectory, "dbus-session"); fileExists(runUserSessionDbusFile) {
+			// if /run/user/<uid>/dbus-session exists, it's a
+			// text file // containing the address of the socket, e.g.:
+			// DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-E1c73yNqrG
+
+			if f, err := ioutil.ReadFile(runUserSessionDbusFile); err == nil {
+				fileContent := string(f)
+
+				prefix := "DBUS_SESSION_BUS_ADDRESS="
+
+				if strings.HasPrefix(fileContent, prefix) {
+					address := strings.TrimRight(strings.TrimPrefix(fileContent, prefix), "\n\r")
+					return address
+				}
+			}
+		}
+	}
+	return ""
+}
+
+func getRuntimeDirectory() (string, error) {
+	if currentUser, err := user.Current(); err != nil {
+		return "", err
+	} else {
+		return fmt.Sprintf("/run/user/%s", currentUser.Uid), nil
+	}
+}
+
+func fileExists(filename string) bool {
+	if _, err := os.Stat(filename); !os.IsNotExist(err) {
+		return true
+	} else {
+		return false
+	}
+}
diff --git a/vendor/github.com/godbus/dbus/v5/conn_unix.go b/vendor/github.com/godbus/dbus/v5/conn_unix.go
new file mode 100644
index 000000000..58aee7d2a
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/conn_unix.go
@@ -0,0 +1,17 @@
+//+build !windows,!solaris,!darwin
+
+package dbus
+
+import (
+	"os"
+)
+
+const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket"
+
+func getSystemBusPlatformAddress() string {
+	address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
+	if address != "" {
+		return address
+	}
+	return defaultSystemBusAddress
+}
diff --git a/vendor/github.com/godbus/dbus/v5/conn_windows.go b/vendor/github.com/godbus/dbus/v5/conn_windows.go
new file mode 100644
index 000000000..4291e4519
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/conn_windows.go
@@ -0,0 +1,15 @@
+//+build windows
+
+package dbus
+
+import "os"
+
+const defaultSystemBusAddress = "tcp:host=127.0.0.1,port=12434"
+
+func getSystemBusPlatformAddress() string {
+	address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
+	if address != "" {
+		return address
+	}
+	return defaultSystemBusAddress
+}
diff --git a/vendor/github.com/godbus/dbus/v5/dbus.go b/vendor/github.com/godbus/dbus/v5/dbus.go
new file mode 100644
index 000000000..428923d26
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/dbus.go
@@ -0,0 +1,428 @@
+package dbus
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+	"strings"
+)
+
+var (
+	byteType        = reflect.TypeOf(byte(0))
+	boolType        = reflect.TypeOf(false)
+	uint8Type       = reflect.TypeOf(uint8(0))
+	int16Type       = reflect.TypeOf(int16(0))
+	uint16Type      = reflect.TypeOf(uint16(0))
+	intType         = reflect.TypeOf(int(0))
+	uintType        = reflect.TypeOf(uint(0))
+	int32Type       = reflect.TypeOf(int32(0))
+	uint32Type      = reflect.TypeOf(uint32(0))
+	int64Type       = reflect.TypeOf(int64(0))
+	uint64Type      = reflect.TypeOf(uint64(0))
+	float64Type     = reflect.TypeOf(float64(0))
+	stringType      = reflect.TypeOf("")
+	signatureType   = reflect.TypeOf(Signature{""})
+	objectPathType  = reflect.TypeOf(ObjectPath(""))
+	variantType     = reflect.TypeOf(Variant{Signature{""}, nil})
+	interfacesType  = reflect.TypeOf([]interface{}{})
+	interfaceType   = reflect.TypeOf((*interface{})(nil)).Elem()
+	unixFDType      = reflect.TypeOf(UnixFD(0))
+	unixFDIndexType = reflect.TypeOf(UnixFDIndex(0))
+)
+
+// An InvalidTypeError signals that a value which cannot be represented in the
+// D-Bus wire format was passed to a function.
+type InvalidTypeError struct {
+	Type reflect.Type
+}
+
+func (e InvalidTypeError) Error() string {
+	return "dbus: invalid type " + e.Type.String()
+}
+
+// Store copies the values contained in src to dest, which must be a slice of
+// pointers. It converts slices of interfaces from src to corresponding structs
+// in dest. An error is returned if the lengths of src and dest or the types of
+// their elements don't match.
+func Store(src []interface{}, dest ...interface{}) error {
+	if len(src) != len(dest) {
+		return errors.New("dbus.Store: length mismatch")
+	}
+
+	for i := range src {
+		if err := storeInterfaces(src[i], dest[i]); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func storeInterfaces(src, dest interface{}) error {
+	return store(reflect.ValueOf(dest), reflect.ValueOf(src))
+}
+
+func store(dest, src reflect.Value) error {
+	if dest.Kind() == reflect.Ptr {
+		return store(dest.Elem(), src)
+	}
+	switch src.Kind() {
+	case reflect.Slice:
+		return storeSlice(dest, src)
+	case reflect.Map:
+		return storeMap(dest, src)
+	default:
+		return storeBase(dest, src)
+	}
+}
+
+func storeBase(dest, src reflect.Value) error {
+	return setDest(dest, src)
+}
+
+func setDest(dest, src reflect.Value) error {
+	if !isVariant(src.Type()) && isVariant(dest.Type()) {
+		//special conversion for dbus.Variant
+		dest.Set(reflect.ValueOf(MakeVariant(src.Interface())))
+		return nil
+	}
+	if isVariant(src.Type()) && !isVariant(dest.Type()) {
+		src = getVariantValue(src)
+		return store(dest, src)
+	}
+	if !src.Type().ConvertibleTo(dest.Type()) {
+		return fmt.Errorf(
+			"dbus.Store: type mismatch: cannot convert %s to %s",
+			src.Type(), dest.Type())
+	}
+	dest.Set(src.Convert(dest.Type()))
+	return nil
+}
+
+func kindsAreCompatible(dest, src reflect.Type) bool {
+	switch {
+	case isVariant(dest):
+		return true
+	case dest.Kind() == reflect.Interface:
+		return true
+	default:
+		return dest.Kind() == src.Kind()
+	}
+}
+
+func isConvertibleTo(dest, src reflect.Type) bool {
+	switch {
+	case isVariant(dest):
+		return true
+	case dest.Kind() == reflect.Interface:
+		return true
+	case dest.Kind() == reflect.Slice:
+		return src.Kind() == reflect.Slice &&
+			isConvertibleTo(dest.Elem(), src.Elem())
+	case dest.Kind() == reflect.Struct:
+		return src == interfacesType
+	default:
+		return src.ConvertibleTo(dest)
+	}
+}
+
+func storeMap(dest, src reflect.Value) error {
+	switch {
+	case !kindsAreCompatible(dest.Type(), src.Type()):
+		return fmt.Errorf(
+			"dbus.Store: type mismatch: "+
+				"map: cannot store a value of %s into %s",
+			src.Type(), dest.Type())
+	case isVariant(dest.Type()):
+		return storeMapIntoVariant(dest, src)
+	case dest.Kind() == reflect.Interface:
+		return storeMapIntoInterface(dest, src)
+	case isConvertibleTo(dest.Type().Key(), src.Type().Key()) &&
+		isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
+		return storeMapIntoMap(dest, src)
+	default:
+		return fmt.Errorf(
+			"dbus.Store: type mismatch: "+
+				"map: cannot convert a value of %s into %s",
+			src.Type(), dest.Type())
+	}
+}
+
+func storeMapIntoVariant(dest, src reflect.Value) error {
+	dv := reflect.MakeMap(src.Type())
+	err := store(dv, src)
+	if err != nil {
+		return err
+	}
+	return storeBase(dest, dv)
+}
+
+func storeMapIntoInterface(dest, src reflect.Value) error {
+	var dv reflect.Value
+	if isVariant(src.Type().Elem()) {
+		//Convert variants to interface{} recursively when converting
+		//to interface{}
+		dv = reflect.MakeMap(
+			reflect.MapOf(src.Type().Key(), interfaceType))
+	} else {
+		dv = reflect.MakeMap(src.Type())
+	}
+	err := store(dv, src)
+	if err != nil {
+		return err
+	}
+	return storeBase(dest, dv)
+}
+
+func storeMapIntoMap(dest, src reflect.Value) error {
+	if dest.IsNil() {
+		dest.Set(reflect.MakeMap(dest.Type()))
+	}
+	keys := src.MapKeys()
+	for _, key := range keys {
+		dkey := key.Convert(dest.Type().Key())
+		dval := reflect.New(dest.Type().Elem()).Elem()
+		err := store(dval, getVariantValue(src.MapIndex(key)))
+		if err != nil {
+			return err
+		}
+		dest.SetMapIndex(dkey, dval)
+	}
+	return nil
+}
+
+func storeSlice(dest, src reflect.Value) error {
+	switch {
+	case src.Type() == interfacesType && dest.Kind() == reflect.Struct:
+		//The decoder always decodes structs as slices of interface{}
+		return storeStruct(dest, src)
+	case !kindsAreCompatible(dest.Type(), src.Type()):
+		return fmt.Errorf(
+			"dbus.Store: type mismatch: "+
+				"slice: cannot store a value of %s into %s",
+			src.Type(), dest.Type())
+	case isVariant(dest.Type()):
+		return storeSliceIntoVariant(dest, src)
+	case dest.Kind() == reflect.Interface:
+		return storeSliceIntoInterface(dest, src)
+	case isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
+		return storeSliceIntoSlice(dest, src)
+	default:
+		return fmt.Errorf(
+			"dbus.Store: type mismatch: "+
+				"slice: cannot convert a value of %s into %s",
+			src.Type(), dest.Type())
+	}
+}
+
+func storeStruct(dest, src reflect.Value) error {
+	if isVariant(dest.Type()) {
+		return storeBase(dest, src)
+	}
+	dval := make([]interface{}, 0, dest.NumField())
+	dtype := dest.Type()
+	for i := 0; i < dest.NumField(); i++ {
+		field := dest.Field(i)
+		ftype := dtype.Field(i)
+		if ftype.PkgPath != "" {
+			continue
+		}
+		if ftype.Tag.Get("dbus") == "-" {
+			continue
+		}
+		dval = append(dval, field.Addr().Interface())
+	}
+	if src.Len() != len(dval) {
+		return fmt.Errorf(
+			"dbus.Store: type mismatch: "+
+				"destination struct does not have "+
+				"enough fields need: %d have: %d",
+			src.Len(), len(dval))
+	}
+	return Store(src.Interface().([]interface{}), dval...)
+}
+
+func storeSliceIntoVariant(dest, src reflect.Value) error {
+	dv := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
+	err := store(dv, src)
+	if err != nil {
+		return err
+	}
+	return storeBase(dest, dv)
+}
+
+func storeSliceIntoInterface(dest, src reflect.Value) error {
+	var dv reflect.Value
+	if isVariant(src.Type().Elem()) {
+		//Convert variants to interface{} recursively when converting
+		//to interface{}
+		dv = reflect.MakeSlice(reflect.SliceOf(interfaceType),
+			src.Len(), src.Cap())
+	} else {
+		dv = reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
+	}
+	err := store(dv, src)
+	if err != nil {
+		return err
+	}
+	return storeBase(dest, dv)
+}
+
+func storeSliceIntoSlice(dest, src reflect.Value) error {
+	if dest.IsNil() || dest.Len() < src.Len() {
+		dest.Set(reflect.MakeSlice(dest.Type(), src.Len(), src.Cap()))
+	}
+	if dest.Len() != src.Len() {
+		return fmt.Errorf(
+			"dbus.Store: type mismatch: "+
+				"slices are different lengths "+
+				"need: %d have: %d",
+			src.Len(), dest.Len())
+	}
+	for i := 0; i < src.Len(); i++ {
+		err := store(dest.Index(i), getVariantValue(src.Index(i)))
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func getVariantValue(in reflect.Value) reflect.Value {
+	if isVariant(in.Type()) {
+		return reflect.ValueOf(in.Interface().(Variant).Value())
+	}
+	return in
+}
+
+func isVariant(t reflect.Type) bool {
+	return t == variantType
+}
+
+// An ObjectPath is an object path as defined by the D-Bus spec.
+type ObjectPath string
+
+// IsValid returns whether the object path is valid.
+func (o ObjectPath) IsValid() bool {
+	s := string(o)
+	if len(s) == 0 {
+		return false
+	}
+	if s[0] != '/' {
+		return false
+	}
+	if s[len(s)-1] == '/' && len(s) != 1 {
+		return false
+	}
+	// probably not used, but technically possible
+	if s == "/" {
+		return true
+	}
+	split := strings.Split(s[1:], "/")
+	for _, v := range split {
+		if len(v) == 0 {
+			return false
+		}
+		for _, c := range v {
+			if !isMemberChar(c) {
+				return false
+			}
+		}
+	}
+	return true
+}
+
+// A UnixFD is a Unix file descriptor sent over the wire. See the package-level
+// documentation for more information about Unix file descriptor passsing.
+type UnixFD int32
+
+// A UnixFDIndex is the representation of a Unix file descriptor in a message.
+type UnixFDIndex uint32
+
+// alignment returns the alignment of values of type t.
+func alignment(t reflect.Type) int {
+	switch t {
+	case variantType:
+		return 1
+	case objectPathType:
+		return 4
+	case signatureType:
+		return 1
+	case interfacesType:
+		return 4
+	}
+	switch t.Kind() {
+	case reflect.Uint8:
+		return 1
+	case reflect.Uint16, reflect.Int16:
+		return 2
+	case reflect.Uint, reflect.Int, reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map:
+		return 4
+	case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct:
+		return 8
+	case reflect.Ptr:
+		return alignment(t.Elem())
+	}
+	return 1
+}
+
+// isKeyType returns whether t is a valid type for a D-Bus dict.
+func isKeyType(t reflect.Type) bool {
+	switch t.Kind() {
+	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
+		reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64,
+		reflect.String, reflect.Uint, reflect.Int:
+
+		return true
+	}
+	return false
+}
+
+// isValidInterface returns whether s is a valid name for an interface.
+func isValidInterface(s string) bool {
+	if len(s) == 0 || len(s) > 255 || s[0] == '.' {
+		return false
+	}
+	elem := strings.Split(s, ".")
+	if len(elem) < 2 {
+		return false
+	}
+	for _, v := range elem {
+		if len(v) == 0 {
+			return false
+		}
+		if v[0] >= '0' && v[0] <= '9' {
+			return false
+		}
+		for _, c := range v {
+			if !isMemberChar(c) {
+				return false
+			}
+		}
+	}
+	return true
+}
+
+// isValidMember returns whether s is a valid name for a member.
+func isValidMember(s string) bool {
+	if len(s) == 0 || len(s) > 255 {
+		return false
+	}
+	i := strings.Index(s, ".")
+	if i != -1 {
+		return false
+	}
+	if s[0] >= '0' && s[0] <= '9' {
+		return false
+	}
+	for _, c := range s {
+		if !isMemberChar(c) {
+			return false
+		}
+	}
+	return true
+}
+
+func isMemberChar(c rune) bool {
+	return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
+		(c >= 'a' && c <= 'z') || c == '_'
+}
diff --git a/vendor/github.com/godbus/dbus/v5/decoder.go b/vendor/github.com/godbus/dbus/v5/decoder.go
new file mode 100644
index 000000000..ede91575b
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/decoder.go
@@ -0,0 +1,286 @@
+package dbus
+
+import (
+	"encoding/binary"
+	"io"
+	"reflect"
+)
+
+type decoder struct {
+	in    io.Reader
+	order binary.ByteOrder
+	pos   int
+}
+
+// newDecoder returns a new decoder that reads values from in. The input is
+// expected to be in the given byte order.
+func newDecoder(in io.Reader, order binary.ByteOrder) *decoder {
+	dec := new(decoder)
+	dec.in = in
+	dec.order = order
+	return dec
+}
+
+// align aligns the input to the given boundary and panics on error.
+func (dec *decoder) align(n int) {
+	if dec.pos%n != 0 {
+		newpos := (dec.pos + n - 1) & ^(n - 1)
+		empty := make([]byte, newpos-dec.pos)
+		if _, err := io.ReadFull(dec.in, empty); err != nil {
+			panic(err)
+		}
+		dec.pos = newpos
+	}
+}
+
+// Calls binary.Read(dec.in, dec.order, v) and panics on read errors.
+func (dec *decoder) binread(v interface{}) {
+	if err := binary.Read(dec.in, dec.order, v); err != nil {
+		panic(err)
+	}
+}
+
+func (dec *decoder) Decode(sig Signature) (vs []interface{}, err error) {
+	defer func() {
+		var ok bool
+		v := recover()
+		if err, ok = v.(error); ok {
+			if err == io.EOF || err == io.ErrUnexpectedEOF {
+				err = FormatError("unexpected EOF")
+			}
+		}
+	}()
+	vs = make([]interface{}, 0)
+	s := sig.str
+	for s != "" {
+		err, rem := validSingle(s, 0)
+		if err != nil {
+			return nil, err
+		}
+		v := dec.decode(s[:len(s)-len(rem)], 0)
+		vs = append(vs, v)
+		s = rem
+	}
+	return vs, nil
+}
+
+func (dec *decoder) decode(s string, depth int) interface{} {
+	dec.align(alignment(typeFor(s)))
+	switch s[0] {
+	case 'y':
+		var b [1]byte
+		if _, err := dec.in.Read(b[:]); err != nil {
+			panic(err)
+		}
+		dec.pos++
+		return b[0]
+	case 'b':
+		i := dec.decode("u", depth).(uint32)
+		switch {
+		case i == 0:
+			return false
+		case i == 1:
+			return true
+		default:
+			panic(FormatError("invalid value for boolean"))
+		}
+	case 'n':
+		var i int16
+		dec.binread(&i)
+		dec.pos += 2
+		return i
+	case 'i':
+		var i int32
+		dec.binread(&i)
+		dec.pos += 4
+		return i
+	case 'x':
+		var i int64
+		dec.binread(&i)
+		dec.pos += 8
+		return i
+	case 'q':
+		var i uint16
+		dec.binread(&i)
+		dec.pos += 2
+		return i
+	case 'u':
+		var i uint32
+		dec.binread(&i)
+		dec.pos += 4
+		return i
+	case 't':
+		var i uint64
+		dec.binread(&i)
+		dec.pos += 8
+		return i
+	case 'd':
+		var f float64
+		dec.binread(&f)
+		dec.pos += 8
+		return f
+	case 's':
+		length := dec.decode("u", depth).(uint32)
+		b := make([]byte, int(length)+1)
+		if _, err := io.ReadFull(dec.in, b); err != nil {
+			panic(err)
+		}
+		dec.pos += int(length) + 1
+		return string(b[:len(b)-1])
+	case 'o':
+		return ObjectPath(dec.decode("s", depth).(string))
+	case 'g':
+		length := dec.decode("y", depth).(byte)
+		b := make([]byte, int(length)+1)
+		if _, err := io.ReadFull(dec.in, b); err != nil {
+			panic(err)
+		}
+		dec.pos += int(length) + 1
+		sig, err := ParseSignature(string(b[:len(b)-1]))
+		if err != nil {
+			panic(err)
+		}
+		return sig
+	case 'v':
+		if depth >= 64 {
+			panic(FormatError("input exceeds container depth limit"))
+		}
+		var variant Variant
+		sig := dec.decode("g", depth).(Signature)
+		if len(sig.str) == 0 {
+			panic(FormatError("variant signature is empty"))
+		}
+		err, rem := validSingle(sig.str, 0)
+		if err != nil {
+			panic(err)
+		}
+		if rem != "" {
+			panic(FormatError("variant signature has multiple types"))
+		}
+		variant.sig = sig
+		variant.value = dec.decode(sig.str, depth+1)
+		return variant
+	case 'h':
+		return UnixFDIndex(dec.decode("u", depth).(uint32))
+	case 'a':
+		if len(s) > 1 && s[1] == '{' {
+			ksig := s[2:3]
+			vsig := s[3 : len(s)-1]
+			v := reflect.MakeMap(reflect.MapOf(typeFor(ksig), typeFor(vsig)))
+			if depth >= 63 {
+				panic(FormatError("input exceeds container depth limit"))
+			}
+			length := dec.decode("u", depth).(uint32)
+			// Even for empty maps, the correct padding must be included
+			dec.align(8)
+			spos := dec.pos
+			for dec.pos < spos+int(length) {
+				dec.align(8)
+				if !isKeyType(v.Type().Key()) {
+					panic(InvalidTypeError{v.Type()})
+				}
+				kv := dec.decode(ksig, depth+2)
+				vv := dec.decode(vsig, depth+2)
+				v.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv))
+			}
+			return v.Interface()
+		}
+		if depth >= 64 {
+			panic(FormatError("input exceeds container depth limit"))
+		}
+		sig := s[1:]
+		length := dec.decode("u", depth).(uint32)
+		// capacity can be determined only for fixed-size element types
+		var capacity int
+		if s := sigByteSize(sig); s != 0 {
+			capacity = int(length) / s
+		}
+		v := reflect.MakeSlice(reflect.SliceOf(typeFor(sig)), 0, capacity)
+		// Even for empty arrays, the correct padding must be included
+		align := alignment(typeFor(s[1:]))
+		if len(s) > 1 && s[1] == '(' {
+			//Special case for arrays of structs
+			//structs decode as a slice of interface{} values
+			//but the dbus alignment does not match this
+			align = 8
+		}
+		dec.align(align)
+		spos := dec.pos
+		for dec.pos < spos+int(length) {
+			ev := dec.decode(s[1:], depth+1)
+			v = reflect.Append(v, reflect.ValueOf(ev))
+		}
+		return v.Interface()
+	case '(':
+		if depth >= 64 {
+			panic(FormatError("input exceeds container depth limit"))
+		}
+		dec.align(8)
+		v := make([]interface{}, 0)
+		s = s[1 : len(s)-1]
+		for s != "" {
+			err, rem := validSingle(s, 0)
+			if err != nil {
+				panic(err)
+			}
+			ev := dec.decode(s[:len(s)-len(rem)], depth+1)
+			v = append(v, ev)
+			s = rem
+		}
+		return v
+	default:
+		panic(SignatureError{Sig: s})
+	}
+}
+
+// sigByteSize tries to calculates size of the given signature in bytes.
+//
+// It returns zero when it can't, for example when it contains non-fixed size
+// types such as strings, maps and arrays that require reading of the transmitted
+// data, for that we would need to implement the unread method for Decoder first.
+func sigByteSize(sig string) int {
+	var total int
+	for offset := 0; offset < len(sig); {
+		switch sig[offset] {
+		case 'y':
+			total += 1
+			offset += 1
+		case 'n', 'q':
+			total += 2
+			offset += 1
+		case 'b', 'i', 'u', 'h':
+			total += 4
+			offset += 1
+		case 'x', 't', 'd':
+			total += 8
+			offset += 1
+		case '(':
+			i := 1
+			depth := 1
+			for i < len(sig[offset:]) && depth != 0 {
+				if sig[offset+i] == '(' {
+					depth++
+				} else if sig[offset+i] == ')' {
+					depth--
+				}
+				i++
+			}
+			s := sigByteSize(sig[offset+1 : offset+i-1])
+			if s == 0 {
+				return 0
+			}
+			total += s
+			offset += i
+		default:
+			return 0
+		}
+	}
+	return total
+}
+
+// A FormatError is an error in the wire format.
+type FormatError string
+
+func (e FormatError) Error() string {
+	return "dbus: wire format error: " + string(e)
+}
diff --git a/vendor/github.com/godbus/dbus/v5/default_handler.go b/vendor/github.com/godbus/dbus/v5/default_handler.go
new file mode 100644
index 000000000..6d8bf32f9
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/default_handler.go
@@ -0,0 +1,328 @@
+package dbus
+
+import (
+	"bytes"
+	"reflect"
+	"strings"
+	"sync"
+)
+
+func newIntrospectIntf(h *defaultHandler) *exportedIntf {
+	methods := make(map[string]Method)
+	methods["Introspect"] = exportedMethod{
+		reflect.ValueOf(func(msg Message) (string, *Error) {
+			path := msg.Headers[FieldPath].value.(ObjectPath)
+			return h.introspectPath(path), nil
+		}),
+	}
+	return newExportedIntf(methods, true)
+}
+
+//NewDefaultHandler returns an instance of the default
+//call handler. This is useful if you want to implement only
+//one of the two handlers but not both.
+//
+// Deprecated: this is the default value, don't use it, it will be unexported.
+func NewDefaultHandler() *defaultHandler {
+	h := &defaultHandler{
+		objects:     make(map[ObjectPath]*exportedObj),
+		defaultIntf: make(map[string]*exportedIntf),
+	}
+	h.defaultIntf["org.freedesktop.DBus.Introspectable"] = newIntrospectIntf(h)
+	return h
+}
+
+type defaultHandler struct {
+	sync.RWMutex
+	objects     map[ObjectPath]*exportedObj
+	defaultIntf map[string]*exportedIntf
+}
+
+func (h *defaultHandler) PathExists(path ObjectPath) bool {
+	_, ok := h.objects[path]
+	return ok
+}
+
+func (h *defaultHandler) introspectPath(path ObjectPath) string {
+	subpath := make(map[string]struct{})
+	var xml bytes.Buffer
+	xml.WriteString("<node>")
+	for obj := range h.objects {
+		p := string(path)
+		if p != "/" {
+			p += "/"
+		}
+		if strings.HasPrefix(string(obj), p) {
+			node_name := strings.Split(string(obj[len(p):]), "/")[0]
+			subpath[node_name] = struct{}{}
+		}
+	}
+	for s := range subpath {
+		xml.WriteString("\n\t<node name=\"" + s + "\"/>")
+	}
+	xml.WriteString("\n</node>")
+	return xml.String()
+}
+
+func (h *defaultHandler) LookupObject(path ObjectPath) (ServerObject, bool) {
+	h.RLock()
+	defer h.RUnlock()
+	object, ok := h.objects[path]
+	if ok {
+		return object, ok
+	}
+
+	// If an object wasn't found for this exact path,
+	// look for a matching subtree registration
+	subtreeObject := newExportedObject()
+	path = path[:strings.LastIndex(string(path), "/")]
+	for len(path) > 0 {
+		object, ok = h.objects[path]
+		if ok {
+			for name, iface := range object.interfaces {
+				// Only include this handler if it registered for the subtree
+				if iface.isFallbackInterface() {
+					subtreeObject.interfaces[name] = iface
+				}
+			}
+			break
+		}
+
+		path = path[:strings.LastIndex(string(path), "/")]
+	}
+
+	for name, intf := range h.defaultIntf {
+		if _, exists := subtreeObject.interfaces[name]; exists {
+			continue
+		}
+		subtreeObject.interfaces[name] = intf
+	}
+
+	return subtreeObject, true
+}
+
+func (h *defaultHandler) AddObject(path ObjectPath, object *exportedObj) {
+	h.Lock()
+	h.objects[path] = object
+	h.Unlock()
+}
+
+func (h *defaultHandler) DeleteObject(path ObjectPath) {
+	h.Lock()
+	delete(h.objects, path)
+	h.Unlock()
+}
+
+type exportedMethod struct {
+	reflect.Value
+}
+
+func (m exportedMethod) Call(args ...interface{}) ([]interface{}, error) {
+	t := m.Type()
+
+	params := make([]reflect.Value, len(args))
+	for i := 0; i < len(args); i++ {
+		params[i] = reflect.ValueOf(args[i]).Elem()
+	}
+
+	ret := m.Value.Call(params)
+
+	err := ret[t.NumOut()-1].Interface().(*Error)
+	ret = ret[:t.NumOut()-1]
+	out := make([]interface{}, len(ret))
+	for i, val := range ret {
+		out[i] = val.Interface()
+	}
+	if err == nil {
+		//concrete type to interface nil is a special case
+		return out, nil
+	}
+	return out, err
+}
+
+func (m exportedMethod) NumArguments() int {
+	return m.Value.Type().NumIn()
+}
+
+func (m exportedMethod) ArgumentValue(i int) interface{} {
+	return reflect.Zero(m.Type().In(i)).Interface()
+}
+
+func (m exportedMethod) NumReturns() int {
+	return m.Value.Type().NumOut()
+}
+
+func (m exportedMethod) ReturnValue(i int) interface{} {
+	return reflect.Zero(m.Type().Out(i)).Interface()
+}
+
+func newExportedObject() *exportedObj {
+	return &exportedObj{
+		interfaces: make(map[string]*exportedIntf),
+	}
+}
+
+type exportedObj struct {
+	mu         sync.RWMutex
+	interfaces map[string]*exportedIntf
+}
+
+func (obj *exportedObj) LookupInterface(name string) (Interface, bool) {
+	if name == "" {
+		return obj, true
+	}
+	obj.mu.RLock()
+	defer obj.mu.RUnlock()
+	intf, exists := obj.interfaces[name]
+	return intf, exists
+}
+
+func (obj *exportedObj) AddInterface(name string, iface *exportedIntf) {
+	obj.mu.Lock()
+	defer obj.mu.Unlock()
+	obj.interfaces[name] = iface
+}
+
+func (obj *exportedObj) DeleteInterface(name string) {
+	obj.mu.Lock()
+	defer obj.mu.Unlock()
+	delete(obj.interfaces, name)
+}
+
+func (obj *exportedObj) LookupMethod(name string) (Method, bool) {
+	obj.mu.RLock()
+	defer obj.mu.RUnlock()
+	for _, intf := range obj.interfaces {
+		method, exists := intf.LookupMethod(name)
+		if exists {
+			return method, exists
+		}
+	}
+	return nil, false
+}
+
+func (obj *exportedObj) isFallbackInterface() bool {
+	return false
+}
+
+func newExportedIntf(methods map[string]Method, includeSubtree bool) *exportedIntf {
+	return &exportedIntf{
+		methods:        methods,
+		includeSubtree: includeSubtree,
+	}
+}
+
+type exportedIntf struct {
+	methods map[string]Method
+
+	// Whether or not this export is for the entire subtree
+	includeSubtree bool
+}
+
+func (obj *exportedIntf) LookupMethod(name string) (Method, bool) {
+	out, exists := obj.methods[name]
+	return out, exists
+}
+
+func (obj *exportedIntf) isFallbackInterface() bool {
+	return obj.includeSubtree
+}
+
+//NewDefaultSignalHandler returns an instance of the default
+//signal handler. This is useful if you want to implement only
+//one of the two handlers but not both.
+//
+// Deprecated: this is the default value, don't use it, it will be unexported.
+func NewDefaultSignalHandler() *defaultSignalHandler {
+	return &defaultSignalHandler{}
+}
+
+type defaultSignalHandler struct {
+	mu      sync.RWMutex
+	closed  bool
+	signals []*signalChannelData
+}
+
+func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) {
+	sh.mu.RLock()
+	defer sh.mu.RUnlock()
+	if sh.closed {
+		return
+	}
+	for _, scd := range sh.signals {
+		scd.deliver(signal)
+	}
+}
+
+func (sh *defaultSignalHandler) Terminate() {
+	sh.mu.Lock()
+	defer sh.mu.Unlock()
+	if sh.closed {
+		return
+	}
+
+	for _, scd := range sh.signals {
+		scd.close()
+		close(scd.ch)
+	}
+	sh.closed = true
+	sh.signals = nil
+}
+
+func (sh *defaultSignalHandler) AddSignal(ch chan<- *Signal) {
+	sh.mu.Lock()
+	defer sh.mu.Unlock()
+	if sh.closed {
+		return
+	}
+	sh.signals = append(sh.signals, &signalChannelData{
+		ch:   ch,
+		done: make(chan struct{}),
+	})
+}
+
+func (sh *defaultSignalHandler) RemoveSignal(ch chan<- *Signal) {
+	sh.mu.Lock()
+	defer sh.mu.Unlock()
+	if sh.closed {
+		return
+	}
+	for i := len(sh.signals) - 1; i >= 0; i-- {
+		if ch == sh.signals[i].ch {
+			sh.signals[i].close()
+			copy(sh.signals[i:], sh.signals[i+1:])
+			sh.signals[len(sh.signals)-1] = nil
+			sh.signals = sh.signals[:len(sh.signals)-1]
+		}
+	}
+}
+
+type signalChannelData struct {
+	wg   sync.WaitGroup
+	ch   chan<- *Signal
+	done chan struct{}
+}
+
+func (scd *signalChannelData) deliver(signal *Signal) {
+	select {
+	case scd.ch <- signal:
+	case <-scd.done:
+		return
+	default:
+		scd.wg.Add(1)
+		go scd.deferredDeliver(signal)
+	}
+}
+
+func (scd *signalChannelData) deferredDeliver(signal *Signal) {
+	select {
+	case scd.ch <- signal:
+	case <-scd.done:
+	}
+	scd.wg.Done()
+}
+
+func (scd *signalChannelData) close() {
+	close(scd.done)
+	scd.wg.Wait() // wait until all spawned goroutines return
+}
diff --git a/vendor/github.com/godbus/dbus/v5/doc.go b/vendor/github.com/godbus/dbus/v5/doc.go
new file mode 100644
index 000000000..ade1df951
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/doc.go
@@ -0,0 +1,69 @@
+/*
+Package dbus implements bindings to the D-Bus message bus system.
+
+To use the message bus API, you first need to connect to a bus (usually the
+session or system bus). The acquired connection then can be used to call methods
+on remote objects and emit or receive signals. Using the Export method, you can
+arrange D-Bus methods calls to be directly translated to method calls on a Go
+value.
+
+Conversion Rules
+
+For outgoing messages, Go types are automatically converted to the
+corresponding D-Bus types. The following types are directly encoded as their
+respective D-Bus equivalents:
+
+     Go type     | D-Bus type
+     ------------+-----------
+     byte        | BYTE
+     bool        | BOOLEAN
+     int16       | INT16
+     uint16      | UINT16
+     int         | INT32
+     uint        | UINT32
+     int32       | INT32
+     uint32      | UINT32
+     int64       | INT64
+     uint64      | UINT64
+     float64     | DOUBLE
+     string      | STRING
+     ObjectPath  | OBJECT_PATH
+     Signature   | SIGNATURE
+     Variant     | VARIANT
+     interface{} | VARIANT
+     UnixFDIndex | UNIX_FD
+
+Slices and arrays encode as ARRAYs of their element type.
+
+Maps encode as DICTs, provided that their key type can be used as a key for
+a DICT.
+
+Structs other than Variant and Signature encode as a STRUCT containing their
+exported fields. Fields whose tags contain `dbus:"-"` and unexported fields will
+be skipped.
+
+Pointers encode as the value they're pointed to.
+
+Types convertible to one of the base types above will be mapped as the
+base type.
+
+Trying to encode any other type or a slice, map or struct containing an
+unsupported type will result in an InvalidTypeError.
+
+For incoming messages, the inverse of these rules are used, with the exception
+of STRUCTs. Incoming STRUCTS are represented as a slice of empty interfaces
+containing the struct fields in the correct order. The Store function can be
+used to convert such values to Go structs.
+
+Unix FD passing
+
+Handling Unix file descriptors deserves special mention. To use them, you should
+first check that they are supported on a connection by calling SupportsUnixFDs.
+If it returns true, all method of Connection will translate messages containing
+UnixFD's to messages that are accompanied by the given file descriptors with the
+UnixFD values being substituted by the correct indices. Similarly, the indices
+of incoming messages are automatically resolved. It shouldn't be necessary to use
+UnixFDIndex.
+
+*/
+package dbus
diff --git a/vendor/github.com/godbus/dbus/v5/encoder.go b/vendor/github.com/godbus/dbus/v5/encoder.go
new file mode 100644
index 000000000..adfbb75c5
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/encoder.go
@@ -0,0 +1,210 @@
+package dbus
+
+import (
+	"bytes"
+	"encoding/binary"
+	"io"
+	"reflect"
+)
+
+// An encoder encodes values to the D-Bus wire format.
+type encoder struct {
+	out   io.Writer
+	order binary.ByteOrder
+	pos   int
+}
+
+// NewEncoder returns a new encoder that writes to out in the given byte order.
+func newEncoder(out io.Writer, order binary.ByteOrder) *encoder {
+	return newEncoderAtOffset(out, 0, order)
+}
+
+// newEncoderAtOffset returns a new encoder that writes to out in the given
+// byte order. Specify the offset to initialize pos for proper alignment
+// computation.
+func newEncoderAtOffset(out io.Writer, offset int, order binary.ByteOrder) *encoder {
+	enc := new(encoder)
+	enc.out = out
+	enc.order = order
+	enc.pos = offset
+	return enc
+}
+
+// Aligns the next output to be on a multiple of n. Panics on write errors.
+func (enc *encoder) align(n int) {
+	pad := enc.padding(0, n)
+	if pad > 0 {
+		empty := make([]byte, pad)
+		if _, err := enc.out.Write(empty); err != nil {
+			panic(err)
+		}
+		enc.pos += pad
+	}
+}
+
+// pad returns the number of bytes of padding, based on current position and additional offset.
+// and alignment.
+func (enc *encoder) padding(offset, algn int) int {
+	abs := enc.pos + offset
+	if abs%algn != 0 {
+		newabs := (abs + algn - 1) & ^(algn - 1)
+		return newabs - abs
+	}
+	return 0
+}
+
+// Calls binary.Write(enc.out, enc.order, v) and panics on write errors.
+func (enc *encoder) binwrite(v interface{}) {
+	if err := binary.Write(enc.out, enc.order, v); err != nil {
+		panic(err)
+	}
+}
+
+// Encode encodes the given values to the underlying reader. All written values
+// are aligned properly as required by the D-Bus spec.
+func (enc *encoder) Encode(vs ...interface{}) (err error) {
+	defer func() {
+		err, _ = recover().(error)
+	}()
+	for _, v := range vs {
+		enc.encode(reflect.ValueOf(v), 0)
+	}
+	return nil
+}
+
+// encode encodes the given value to the writer and panics on error. depth holds
+// the depth of the container nesting.
+func (enc *encoder) encode(v reflect.Value, depth int) {
+	enc.align(alignment(v.Type()))
+	switch v.Kind() {
+	case reflect.Uint8:
+		var b [1]byte
+		b[0] = byte(v.Uint())
+		if _, err := enc.out.Write(b[:]); err != nil {
+			panic(err)
+		}
+		enc.pos++
+	case reflect.Bool:
+		if v.Bool() {
+			enc.encode(reflect.ValueOf(uint32(1)), depth)
+		} else {
+			enc.encode(reflect.ValueOf(uint32(0)), depth)
+		}
+	case reflect.Int16:
+		enc.binwrite(int16(v.Int()))
+		enc.pos += 2
+	case reflect.Uint16:
+		enc.binwrite(uint16(v.Uint()))
+		enc.pos += 2
+	case reflect.Int, reflect.Int32:
+		enc.binwrite(int32(v.Int()))
+		enc.pos += 4
+	case reflect.Uint, reflect.Uint32:
+		enc.binwrite(uint32(v.Uint()))
+		enc.pos += 4
+	case reflect.Int64:
+		enc.binwrite(v.Int())
+		enc.pos += 8
+	case reflect.Uint64:
+		enc.binwrite(v.Uint())
+		enc.pos += 8
+	case reflect.Float64:
+		enc.binwrite(v.Float())
+		enc.pos += 8
+	case reflect.String:
+		enc.encode(reflect.ValueOf(uint32(len(v.String()))), depth)
+		b := make([]byte, v.Len()+1)
+		copy(b, v.String())
+		b[len(b)-1] = 0
+		n, err := enc.out.Write(b)
+		if err != nil {
+			panic(err)
+		}
+		enc.pos += n
+	case reflect.Ptr:
+		enc.encode(v.Elem(), depth)
+	case reflect.Slice, reflect.Array:
+		if depth >= 64 {
+			panic(FormatError("input exceeds container depth limit"))
+		}
+		// Lookahead offset: 4 bytes for uint32 length (with alignment),
+		// plus alignment for elements.
+		n := enc.padding(0, 4) + 4
+		offset := enc.pos + n + enc.padding(n, alignment(v.Type().Elem()))
+
+		var buf bytes.Buffer
+		bufenc := newEncoderAtOffset(&buf, offset, enc.order)
+
+		for i := 0; i < v.Len(); i++ {
+			bufenc.encode(v.Index(i), depth+1)
+		}
+		enc.encode(reflect.ValueOf(uint32(buf.Len())), depth)
+		length := buf.Len()
+		enc.align(alignment(v.Type().Elem()))
+		if _, err := buf.WriteTo(enc.out); err != nil {
+			panic(err)
+		}
+		enc.pos += length
+	case reflect.Struct:
+		if depth >= 64 && v.Type() != signatureType {
+			panic(FormatError("input exceeds container depth limit"))
+		}
+		switch t := v.Type(); t {
+		case signatureType:
+			str := v.Field(0)
+			enc.encode(reflect.ValueOf(byte(str.Len())), depth+1)
+			b := make([]byte, str.Len()+1)
+			copy(b, str.String())
+			b[len(b)-1] = 0
+			n, err := enc.out.Write(b)
+			if err != nil {
+				panic(err)
+			}
+			enc.pos += n
+		case variantType:
+			variant := v.Interface().(Variant)
+			enc.encode(reflect.ValueOf(variant.sig), depth+1)
+			enc.encode(reflect.ValueOf(variant.value), depth+1)
+		default:
+			for i := 0; i < v.Type().NumField(); i++ {
+				field := t.Field(i)
+				if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
+					enc.encode(v.Field(i), depth+1)
+				}
+			}
+		}
+	case reflect.Map:
+		// Maps are arrays of structures, so they actually increase the depth by
+		// 2.
+		if depth >= 63 {
+			panic(FormatError("input exceeds container depth limit"))
+		}
+		if !isKeyType(v.Type().Key()) {
+			panic(InvalidTypeError{v.Type()})
+		}
+		keys := v.MapKeys()
+		// Lookahead offset: 4 bytes for uint32 length (with alignment),
+		// plus 8-byte alignment
+		n := enc.padding(0, 4) + 4
+		offset := enc.pos + n + enc.padding(n, 8)
+
+		var buf bytes.Buffer
+		bufenc := newEncoderAtOffset(&buf, offset, enc.order)
+		for _, k := range keys {
+			bufenc.align(8)
+			bufenc.encode(k, depth+2)
+			bufenc.encode(v.MapIndex(k), depth+2)
+		}
+		enc.encode(reflect.ValueOf(uint32(buf.Len())), depth)
+		length := buf.Len()
+		enc.align(8)
+		if _, err := buf.WriteTo(enc.out); err != nil {
+			panic(err)
+		}
+		enc.pos += length
+	case reflect.Interface:
+		enc.encode(reflect.ValueOf(MakeVariant(v.Interface())), depth)
+	default:
+		panic(InvalidTypeError{v.Type()})
+	}
+}
diff --git a/vendor/github.com/godbus/dbus/v5/export.go b/vendor/github.com/godbus/dbus/v5/export.go
new file mode 100644
index 000000000..c277ab142
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/export.go
@@ -0,0 +1,412 @@
+package dbus
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+	"strings"
+)
+
+var (
+	ErrMsgInvalidArg = Error{
+		"org.freedesktop.DBus.Error.InvalidArgs",
+		[]interface{}{"Invalid type / number of args"},
+	}
+	ErrMsgNoObject = Error{
+		"org.freedesktop.DBus.Error.NoSuchObject",
+		[]interface{}{"No such object"},
+	}
+	ErrMsgUnknownMethod = Error{
+		"org.freedesktop.DBus.Error.UnknownMethod",
+		[]interface{}{"Unknown / invalid method"},
+	}
+	ErrMsgUnknownInterface = Error{
+		"org.freedesktop.DBus.Error.UnknownInterface",
+		[]interface{}{"Object does not implement the interface"},
+	}
+)
+
+func MakeFailedError(err error) *Error {
+	return &Error{
+		"org.freedesktop.DBus.Error.Failed",
+		[]interface{}{err.Error()},
+	}
+}
+
+// Sender is a type which can be used in exported methods to receive the message
+// sender.
+type Sender string
+
+func computeMethodName(name string, mapping map[string]string) string {
+	newname, ok := mapping[name]
+	if ok {
+		name = newname
+	}
+	return name
+}
+
+func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Value {
+	if in == nil {
+		return nil
+	}
+	methods := make(map[string]reflect.Value)
+	val := reflect.ValueOf(in)
+	typ := val.Type()
+	for i := 0; i < typ.NumMethod(); i++ {
+		methtype := typ.Method(i)
+		method := val.Method(i)
+		t := method.Type()
+		// only track valid methods must return *Error as last arg
+		// and must be exported
+		if t.NumOut() == 0 ||
+			t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) ||
+			methtype.PkgPath != "" {
+			continue
+		}
+		// map names while building table
+		methods[computeMethodName(methtype.Name, mapping)] = method
+	}
+	return methods
+}
+
+func standardMethodArgumentDecode(m Method, sender string, msg *Message, body []interface{}) ([]interface{}, error) {
+	pointers := make([]interface{}, m.NumArguments())
+	decode := make([]interface{}, 0, len(body))
+
+	for i := 0; i < m.NumArguments(); i++ {
+		tp := reflect.TypeOf(m.ArgumentValue(i))
+		val := reflect.New(tp)
+		pointers[i] = val.Interface()
+		if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
+			val.Elem().SetString(sender)
+		} else if tp == reflect.TypeOf((*Message)(nil)).Elem() {
+			val.Elem().Set(reflect.ValueOf(*msg))
+		} else {
+			decode = append(decode, pointers[i])
+		}
+	}
+
+	if len(decode) != len(body) {
+		return nil, ErrMsgInvalidArg
+	}
+
+	if err := Store(body, decode...); err != nil {
+		return nil, ErrMsgInvalidArg
+	}
+
+	return pointers, nil
+}
+
+func (conn *Conn) decodeArguments(m Method, sender string, msg *Message) ([]interface{}, error) {
+	if decoder, ok := m.(ArgumentDecoder); ok {
+		return decoder.DecodeArguments(conn, sender, msg, msg.Body)
+	}
+	return standardMethodArgumentDecode(m, sender, msg, msg.Body)
+}
+
+// handleCall handles the given method call (i.e. looks if it's one of the
+// pre-implemented ones and searches for a corresponding handler if not).
+func (conn *Conn) handleCall(msg *Message) {
+	name := msg.Headers[FieldMember].value.(string)
+	path := msg.Headers[FieldPath].value.(ObjectPath)
+	ifaceName, _ := msg.Headers[FieldInterface].value.(string)
+	sender, hasSender := msg.Headers[FieldSender].value.(string)
+	serial := msg.serial
+	if ifaceName == "org.freedesktop.DBus.Peer" {
+		switch name {
+		case "Ping":
+			conn.sendReply(sender, serial)
+		case "GetMachineId":
+			conn.sendReply(sender, serial, conn.uuid)
+		default:
+			conn.sendError(ErrMsgUnknownMethod, sender, serial)
+		}
+		return
+	}
+	if len(name) == 0 {
+		conn.sendError(ErrMsgUnknownMethod, sender, serial)
+	}
+
+	object, ok := conn.handler.LookupObject(path)
+	if !ok {
+		conn.sendError(ErrMsgNoObject, sender, serial)
+		return
+	}
+
+	iface, exists := object.LookupInterface(ifaceName)
+	if !exists {
+		conn.sendError(ErrMsgUnknownInterface, sender, serial)
+		return
+	}
+
+	m, exists := iface.LookupMethod(name)
+	if !exists {
+		conn.sendError(ErrMsgUnknownMethod, sender, serial)
+		return
+	}
+	args, err := conn.decodeArguments(m, sender, msg)
+	if err != nil {
+		conn.sendError(err, sender, serial)
+		return
+	}
+
+	ret, err := m.Call(args...)
+	if err != nil {
+		conn.sendError(err, sender, serial)
+		return
+	}
+
+	if msg.Flags&FlagNoReplyExpected == 0 {
+		reply := new(Message)
+		reply.Type = TypeMethodReply
+		reply.serial = conn.getSerial()
+		reply.Headers = make(map[HeaderField]Variant)
+		if hasSender {
+			reply.Headers[FieldDestination] = msg.Headers[FieldSender]
+		}
+		reply.Headers[FieldReplySerial] = MakeVariant(msg.serial)
+		reply.Body = make([]interface{}, len(ret))
+		for i := 0; i < len(ret); i++ {
+			reply.Body[i] = ret[i]
+		}
+		reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
+
+		conn.sendMessageAndIfClosed(reply, nil)
+	}
+}
+
+// Emit emits the given signal on the message bus. The name parameter must be
+// formatted as "interface.member", e.g., "org.freedesktop.DBus.NameLost".
+func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) error {
+	if !path.IsValid() {
+		return errors.New("dbus: invalid object path")
+	}
+	i := strings.LastIndex(name, ".")
+	if i == -1 {
+		return errors.New("dbus: invalid method name")
+	}
+	iface := name[:i]
+	member := name[i+1:]
+	if !isValidMember(member) {
+		return errors.New("dbus: invalid method name")
+	}
+	if !isValidInterface(iface) {
+		return errors.New("dbus: invalid interface name")
+	}
+	msg := new(Message)
+	msg.Type = TypeSignal
+	msg.serial = conn.getSerial()
+	msg.Headers = make(map[HeaderField]Variant)
+	msg.Headers[FieldInterface] = MakeVariant(iface)
+	msg.Headers[FieldMember] = MakeVariant(member)
+	msg.Headers[FieldPath] = MakeVariant(path)
+	msg.Body = values
+	if len(values) > 0 {
+		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
+	}
+
+	var closed bool
+	conn.sendMessageAndIfClosed(msg, func() {
+		closed = true
+	})
+	if closed {
+		return ErrClosed
+	}
+	return nil
+}
+
+// Export registers the given value to be exported as an object on the
+// message bus.
+//
+// If a method call on the given path and interface is received, an exported
+// method with the same name is called with v as the receiver if the
+// parameters match and the last return value is of type *Error. If this
+// *Error is not nil, it is sent back to the caller as an error.
+// Otherwise, a method reply is sent with the other return values as its body.
+//
+// Any parameters with the special type Sender are set to the sender of the
+// dbus message when the method is called. Parameters of this type do not
+// contribute to the dbus signature of the method (i.e. the method is exposed
+// as if the parameters of type Sender were not there).
+//
+// Similarly, any parameters with the type Message are set to the raw message
+// received on the bus. Again, parameters of this type do not contribute to the
+// dbus signature of the method.
+//
+// Every method call is executed in a new goroutine, so the method may be called
+// in multiple goroutines at once.
+//
+// Method calls on the interface org.freedesktop.DBus.Peer will be automatically
+// handled for every object.
+//
+// Passing nil as the first parameter will cause conn to cease handling calls on
+// the given combination of path and interface.
+//
+// Export returns an error if path is not a valid path name.
+func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
+	return conn.ExportWithMap(v, nil, path, iface)
+}
+
+// ExportWithMap works exactly like Export but provides the ability to remap
+// method names (e.g. export a lower-case method).
+//
+// The keys in the map are the real method names (exported on the struct), and
+// the values are the method names to be exported on DBus.
+func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
+	return conn.export(getMethods(v, mapping), path, iface, false)
+}
+
+// ExportSubtree works exactly like Export but registers the given value for
+// an entire subtree rather under the root path provided.
+//
+// In order to make this useful, one parameter in each of the value's exported
+// methods should be a Message, in which case it will contain the raw message
+// (allowing one to get access to the path that caused the method to be called).
+//
+// Note that more specific export paths take precedence over less specific. For
+// example, a method call using the ObjectPath /foo/bar/baz will call a method
+// exported on /foo/bar before a method exported on /foo.
+func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) error {
+	return conn.ExportSubtreeWithMap(v, nil, path, iface)
+}
+
+// ExportSubtreeWithMap works exactly like ExportSubtree but provides the
+// ability to remap method names (e.g. export a lower-case method).
+//
+// The keys in the map are the real method names (exported on the struct), and
+// the values are the method names to be exported on DBus.
+func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
+	return conn.export(getMethods(v, mapping), path, iface, true)
+}
+
+// ExportMethodTable like Export registers the given methods as an object
+// on the message bus. Unlike Export the it uses a method table to define
+// the object instead of a native go object.
+//
+// The method table is a map from method name to function closure
+// representing the method. This allows an object exported on the bus to not
+// necessarily be a native go object. It can be useful for generating exposed
+// methods on the fly.
+//
+// Any non-function objects in the method table are ignored.
+func (conn *Conn) ExportMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
+	return conn.exportMethodTable(methods, path, iface, false)
+}
+
+// Like ExportSubtree, but with the same caveats as ExportMethodTable.
+func (conn *Conn) ExportSubtreeMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
+	return conn.exportMethodTable(methods, path, iface, true)
+}
+
+func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectPath, iface string, includeSubtree bool) error {
+	out := make(map[string]reflect.Value)
+	for name, method := range methods {
+		rval := reflect.ValueOf(method)
+		if rval.Kind() != reflect.Func {
+			continue
+		}
+		t := rval.Type()
+		// only track valid methods must return *Error as last arg
+		if t.NumOut() == 0 ||
+			t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) {
+			continue
+		}
+		out[name] = rval
+	}
+	return conn.export(out, path, iface, includeSubtree)
+}
+
+func (conn *Conn) unexport(h *defaultHandler, path ObjectPath, iface string) error {
+	if h.PathExists(path) {
+		obj := h.objects[path]
+		obj.DeleteInterface(iface)
+		if len(obj.interfaces) == 0 {
+			h.DeleteObject(path)
+		}
+	}
+	return nil
+}
+
+// exportWithMap is the worker function for all exports/registrations.
+func (conn *Conn) export(methods map[string]reflect.Value, path ObjectPath, iface string, includeSubtree bool) error {
+	h, ok := conn.handler.(*defaultHandler)
+	if !ok {
+		return fmt.Errorf(
+			`dbus: export only allowed on the default hander handler have %T"`,
+			conn.handler)
+	}
+
+	if !path.IsValid() {
+		return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
+	}
+
+	// Remove a previous export if the interface is nil
+	if methods == nil {
+		return conn.unexport(h, path, iface)
+	}
+
+	// If this is the first handler for this path, make a new map to hold all
+	// handlers for this path.
+	if !h.PathExists(path) {
+		h.AddObject(path, newExportedObject())
+	}
+
+	exportedMethods := make(map[string]Method)
+	for name, method := range methods {
+		exportedMethods[name] = exportedMethod{method}
+	}
+
+	// Finally, save this handler
+	obj := h.objects[path]
+	obj.AddInterface(iface, newExportedIntf(exportedMethods, includeSubtree))
+
+	return nil
+}
+
+// ReleaseName calls org.freedesktop.DBus.ReleaseName and awaits a response.
+func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) {
+	var r uint32
+	err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r)
+	if err != nil {
+		return 0, err
+	}
+	return ReleaseNameReply(r), nil
+}
+
+// RequestName calls org.freedesktop.DBus.RequestName and awaits a response.
+func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) {
+	var r uint32
+	err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r)
+	if err != nil {
+		return 0, err
+	}
+	return RequestNameReply(r), nil
+}
+
+// ReleaseNameReply is the reply to a ReleaseName call.
+type ReleaseNameReply uint32
+
+const (
+	ReleaseNameReplyReleased ReleaseNameReply = 1 + iota
+	ReleaseNameReplyNonExistent
+	ReleaseNameReplyNotOwner
+)
+
+// RequestNameFlags represents the possible flags for a RequestName call.
+type RequestNameFlags uint32
+
+const (
+	NameFlagAllowReplacement RequestNameFlags = 1 << iota
+	NameFlagReplaceExisting
+	NameFlagDoNotQueue
+)
+
+// RequestNameReply is the reply to a RequestName call.
+type RequestNameReply uint32
+
+const (
+	RequestNameReplyPrimaryOwner RequestNameReply = 1 + iota
+	RequestNameReplyInQueue
+	RequestNameReplyExists
+	RequestNameReplyAlreadyOwner
+)
diff --git a/vendor/github.com/godbus/dbus/v5/go.mod b/vendor/github.com/godbus/dbus/v5/go.mod
new file mode 100644
index 000000000..15b920203
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/go.mod
@@ -0,0 +1,3 @@
+module github.com/godbus/dbus/v5
+
+go 1.12
diff --git a/vendor/github.com/godbus/dbus/v5/go.sum b/vendor/github.com/godbus/dbus/v5/go.sum
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/github.com/godbus/dbus/v5/homedir.go b/vendor/github.com/godbus/dbus/v5/homedir.go
new file mode 100644
index 000000000..0b745f931
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/homedir.go
@@ -0,0 +1,28 @@
+package dbus
+
+import (
+	"os"
+	"sync"
+)
+
+var (
+	homeDir     string
+	homeDirLock sync.Mutex
+)
+
+func getHomeDir() string {
+	homeDirLock.Lock()
+	defer homeDirLock.Unlock()
+
+	if homeDir != "" {
+		return homeDir
+	}
+
+	homeDir = os.Getenv("HOME")
+	if homeDir != "" {
+		return homeDir
+	}
+
+	homeDir = lookupHomeDir()
+	return homeDir
+}
diff --git a/vendor/github.com/godbus/dbus/v5/homedir_dynamic.go b/vendor/github.com/godbus/dbus/v5/homedir_dynamic.go
new file mode 100644
index 000000000..2732081e7
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/homedir_dynamic.go
@@ -0,0 +1,15 @@
+// +build !static_build
+
+package dbus
+
+import (
+	"os/user"
+)
+
+func lookupHomeDir() string {
+	u, err := user.Current()
+	if err != nil {
+		return "/"
+	}
+	return u.HomeDir
+}
diff --git a/vendor/github.com/godbus/dbus/v5/homedir_static.go b/vendor/github.com/godbus/dbus/v5/homedir_static.go
new file mode 100644
index 000000000..b9d9cb552
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/homedir_static.go
@@ -0,0 +1,45 @@
+// +build static_build
+
+package dbus
+
+import (
+	"bufio"
+	"os"
+	"strconv"
+	"strings"
+)
+
+func lookupHomeDir() string {
+	myUid := os.Getuid()
+
+	f, err := os.Open("/etc/passwd")
+	if err != nil {
+		return "/"
+	}
+	defer f.Close()
+
+	s := bufio.NewScanner(f)
+
+	for s.Scan() {
+		if err := s.Err(); err != nil {
+			break
+		}
+
+		line := strings.TrimSpace(s.Text())
+		if line == "" {
+			continue
+		}
+
+		parts := strings.Split(line, ":")
+
+		if len(parts) >= 6 {
+			uid, err := strconv.Atoi(parts[2])
+			if err == nil && uid == myUid {
+				return parts[5]
+			}
+		}
+	}
+
+	// Default to / if we can't get a better value
+	return "/"
+}
diff --git a/vendor/github.com/godbus/dbus/v5/match.go b/vendor/github.com/godbus/dbus/v5/match.go
new file mode 100644
index 000000000..086ee336a
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/match.go
@@ -0,0 +1,62 @@
+package dbus
+
+import (
+	"strings"
+)
+
+// MatchOption specifies option for dbus routing match rule. Options can be constructed with WithMatch* helpers.
+// For full list of available options consult
+// https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
+type MatchOption struct {
+	key   string
+	value string
+}
+
+func formatMatchOptions(options []MatchOption) string {
+	items := make([]string, 0, len(options))
+	for _, option := range options {
+		items = append(items, option.key+"='"+option.value+"'")
+	}
+	return strings.Join(items, ",")
+}
+
+// WithMatchOption creates match option with given key and value
+func WithMatchOption(key, value string) MatchOption {
+	return MatchOption{key, value}
+}
+
+// doesn't make sense to export this option because clients can only
+// subscribe to messages with signal type.
+func withMatchType(typ string) MatchOption {
+	return WithMatchOption("type", typ)
+}
+
+// WithMatchSender sets sender match option.
+func WithMatchSender(sender string) MatchOption {
+	return WithMatchOption("sender", sender)
+}
+
+// WithMatchSender sets interface match option.
+func WithMatchInterface(iface string) MatchOption {
+	return WithMatchOption("interface", iface)
+}
+
+// WithMatchMember sets member match option.
+func WithMatchMember(member string) MatchOption {
+	return WithMatchOption("member", member)
+}
+
+// WithMatchObjectPath creates match option that filters events based on given path
+func WithMatchObjectPath(path ObjectPath) MatchOption {
+	return WithMatchOption("path", string(path))
+}
+
+// WithMatchPathNamespace sets path_namespace match option.
+func WithMatchPathNamespace(namespace ObjectPath) MatchOption {
+	return WithMatchOption("path_namespace", string(namespace))
+}
+
+// WithMatchDestination sets destination match option.
+func WithMatchDestination(destination string) MatchOption {
+	return WithMatchOption("destination", destination)
+}
diff --git a/vendor/github.com/godbus/dbus/v5/message.go b/vendor/github.com/godbus/dbus/v5/message.go
new file mode 100644
index 000000000..6a925367e
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/message.go
@@ -0,0 +1,353 @@
+package dbus
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"io"
+	"reflect"
+	"strconv"
+)
+
+const protoVersion byte = 1
+
+// Flags represents the possible flags of a D-Bus message.
+type Flags byte
+
+const (
+	// FlagNoReplyExpected signals that the message is not expected to generate
+	// a reply. If this flag is set on outgoing messages, any possible reply
+	// will be discarded.
+	FlagNoReplyExpected Flags = 1 << iota
+	// FlagNoAutoStart signals that the message bus should not automatically
+	// start an application when handling this message.
+	FlagNoAutoStart
+	// FlagAllowInteractiveAuthorization may be set on a method call
+	// message to inform the receiving side that the caller is prepared
+	// to wait for interactive authorization, which might take a
+	// considerable time to complete. For instance, if this flag is set,
+	// it would be appropriate to query the user for passwords or
+	// confirmation via Polkit or a similar framework.
+	FlagAllowInteractiveAuthorization
+)
+
+// Type represents the possible types of a D-Bus message.
+type Type byte
+
+const (
+	TypeMethodCall Type = 1 + iota
+	TypeMethodReply
+	TypeError
+	TypeSignal
+	typeMax
+)
+
+func (t Type) String() string {
+	switch t {
+	case TypeMethodCall:
+		return "method call"
+	case TypeMethodReply:
+		return "reply"
+	case TypeError:
+		return "error"
+	case TypeSignal:
+		return "signal"
+	}
+	return "invalid"
+}
+
+// HeaderField represents the possible byte codes for the headers
+// of a D-Bus message.
+type HeaderField byte
+
+const (
+	FieldPath HeaderField = 1 + iota
+	FieldInterface
+	FieldMember
+	FieldErrorName
+	FieldReplySerial
+	FieldDestination
+	FieldSender
+	FieldSignature
+	FieldUnixFDs
+	fieldMax
+)
+
+// An InvalidMessageError describes the reason why a D-Bus message is regarded as
+// invalid.
+type InvalidMessageError string
+
+func (e InvalidMessageError) Error() string {
+	return "dbus: invalid message: " + string(e)
+}
+
+// fieldType are the types of the various header fields.
+var fieldTypes = [fieldMax]reflect.Type{
+	FieldPath:        objectPathType,
+	FieldInterface:   stringType,
+	FieldMember:      stringType,
+	FieldErrorName:   stringType,
+	FieldReplySerial: uint32Type,
+	FieldDestination: stringType,
+	FieldSender:      stringType,
+	FieldSignature:   signatureType,
+	FieldUnixFDs:     uint32Type,
+}
+
+// requiredFields lists the header fields that are required by the different
+// message types.
+var requiredFields = [typeMax][]HeaderField{
+	TypeMethodCall:  {FieldPath, FieldMember},
+	TypeMethodReply: {FieldReplySerial},
+	TypeError:       {FieldErrorName, FieldReplySerial},
+	TypeSignal:      {FieldPath, FieldInterface, FieldMember},
+}
+
+// Message represents a single D-Bus message.
+type Message struct {
+	Type
+	Flags
+	Headers map[HeaderField]Variant
+	Body    []interface{}
+
+	serial uint32
+}
+
+type header struct {
+	Field byte
+	Variant
+}
+
+// DecodeMessage tries to decode a single message in the D-Bus wire format
+// from the given reader. The byte order is figured out from the first byte.
+// The possibly returned error can be an error of the underlying reader, an
+// InvalidMessageError or a FormatError.
+func DecodeMessage(rd io.Reader) (msg *Message, err error) {
+	var order binary.ByteOrder
+	var hlength, length uint32
+	var typ, flags, proto byte
+	var headers []header
+
+	b := make([]byte, 1)
+	_, err = rd.Read(b)
+	if err != nil {
+		return
+	}
+	switch b[0] {
+	case 'l':
+		order = binary.LittleEndian
+	case 'B':
+		order = binary.BigEndian
+	default:
+		return nil, InvalidMessageError("invalid byte order")
+	}
+
+	dec := newDecoder(rd, order)
+	dec.pos = 1
+
+	msg = new(Message)
+	vs, err := dec.Decode(Signature{"yyyuu"})
+	if err != nil {
+		return nil, err
+	}
+	if err = Store(vs, &typ, &flags, &proto, &length, &msg.serial); err != nil {
+		return nil, err
+	}
+	msg.Type = Type(typ)
+	msg.Flags = Flags(flags)
+
+	// get the header length separately because we need it later
+	b = make([]byte, 4)
+	_, err = io.ReadFull(rd, b)
+	if err != nil {
+		return nil, err
+	}
+	binary.Read(bytes.NewBuffer(b), order, &hlength)
+	if hlength+length+16 > 1<<27 {
+		return nil, InvalidMessageError("message is too long")
+	}
+	dec = newDecoder(io.MultiReader(bytes.NewBuffer(b), rd), order)
+	dec.pos = 12
+	vs, err = dec.Decode(Signature{"a(yv)"})
+	if err != nil {
+		return nil, err
+	}
+	if err = Store(vs, &headers); err != nil {
+		return nil, err
+	}
+
+	msg.Headers = make(map[HeaderField]Variant)
+	for _, v := range headers {
+		msg.Headers[HeaderField(v.Field)] = v.Variant
+	}
+
+	dec.align(8)
+	body := make([]byte, int(length))
+	if length != 0 {
+		_, err := io.ReadFull(rd, body)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	if err = msg.IsValid(); err != nil {
+		return nil, err
+	}
+	sig, _ := msg.Headers[FieldSignature].value.(Signature)
+	if sig.str != "" {
+		buf := bytes.NewBuffer(body)
+		dec = newDecoder(buf, order)
+		vs, err := dec.Decode(sig)
+		if err != nil {
+			return nil, err
+		}
+		msg.Body = vs
+	}
+
+	return
+}
+
+// EncodeTo encodes and sends a message to the given writer. The byte order must
+// be either binary.LittleEndian or binary.BigEndian. If the message is not
+// valid or an error occurs when writing, an error is returned.
+func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) error {
+	if err := msg.IsValid(); err != nil {
+		return err
+	}
+	var vs [7]interface{}
+	switch order {
+	case binary.LittleEndian:
+		vs[0] = byte('l')
+	case binary.BigEndian:
+		vs[0] = byte('B')
+	default:
+		return errors.New("dbus: invalid byte order")
+	}
+	body := new(bytes.Buffer)
+	enc := newEncoder(body, order)
+	if len(msg.Body) != 0 {
+		enc.Encode(msg.Body...)
+	}
+	vs[1] = msg.Type
+	vs[2] = msg.Flags
+	vs[3] = protoVersion
+	vs[4] = uint32(len(body.Bytes()))
+	vs[5] = msg.serial
+	headers := make([]header, 0, len(msg.Headers))
+	for k, v := range msg.Headers {
+		headers = append(headers, header{byte(k), v})
+	}
+	vs[6] = headers
+	var buf bytes.Buffer
+	enc = newEncoder(&buf, order)
+	enc.Encode(vs[:]...)
+	enc.align(8)
+	body.WriteTo(&buf)
+	if buf.Len() > 1<<27 {
+		return InvalidMessageError("message is too long")
+	}
+	if _, err := buf.WriteTo(out); err != nil {
+		return err
+	}
+	return nil
+}
+
+// IsValid checks whether msg is a valid message and returns an
+// InvalidMessageError if it is not.
+func (msg *Message) IsValid() error {
+	if msg.Flags & ^(FlagNoAutoStart|FlagNoReplyExpected|FlagAllowInteractiveAuthorization) != 0 {
+		return InvalidMessageError("invalid flags")
+	}
+	if msg.Type == 0 || msg.Type >= typeMax {
+		return InvalidMessageError("invalid message type")
+	}
+	for k, v := range msg.Headers {
+		if k == 0 || k >= fieldMax {
+			return InvalidMessageError("invalid header")
+		}
+		if reflect.TypeOf(v.value) != fieldTypes[k] {
+			return InvalidMessageError("invalid type of header field")
+		}
+	}
+	for _, v := range requiredFields[msg.Type] {
+		if _, ok := msg.Headers[v]; !ok {
+			return InvalidMessageError("missing required header")
+		}
+	}
+	if path, ok := msg.Headers[FieldPath]; ok {
+		if !path.value.(ObjectPath).IsValid() {
+			return InvalidMessageError("invalid path name")
+		}
+	}
+	if iface, ok := msg.Headers[FieldInterface]; ok {
+		if !isValidInterface(iface.value.(string)) {
+			return InvalidMessageError("invalid interface name")
+		}
+	}
+	if member, ok := msg.Headers[FieldMember]; ok {
+		if !isValidMember(member.value.(string)) {
+			return InvalidMessageError("invalid member name")
+		}
+	}
+	if errname, ok := msg.Headers[FieldErrorName]; ok {
+		if !isValidInterface(errname.value.(string)) {
+			return InvalidMessageError("invalid error name")
+		}
+	}
+	if len(msg.Body) != 0 {
+		if _, ok := msg.Headers[FieldSignature]; !ok {
+			return InvalidMessageError("missing signature")
+		}
+	}
+	return nil
+}
+
+// Serial returns the message's serial number. The returned value is only valid
+// for messages received by eavesdropping.
+func (msg *Message) Serial() uint32 {
+	return msg.serial
+}
+
+// String returns a string representation of a message similar to the format of
+// dbus-monitor.
+func (msg *Message) String() string {
+	if err := msg.IsValid(); err != nil {
+		return "<invalid>"
+	}
+	s := msg.Type.String()
+	if v, ok := msg.Headers[FieldSender]; ok {
+		s += " from " + v.value.(string)
+	}
+	if v, ok := msg.Headers[FieldDestination]; ok {
+		s += " to " + v.value.(string)
+	}
+	s += " serial " + strconv.FormatUint(uint64(msg.serial), 10)
+	if v, ok := msg.Headers[FieldReplySerial]; ok {
+		s += " reply_serial " + strconv.FormatUint(uint64(v.value.(uint32)), 10)
+	}
+	if v, ok := msg.Headers[FieldUnixFDs]; ok {
+		s += " unixfds " + strconv.FormatUint(uint64(v.value.(uint32)), 10)
+	}
+	if v, ok := msg.Headers[FieldPath]; ok {
+		s += " path " + string(v.value.(ObjectPath))
+	}
+	if v, ok := msg.Headers[FieldInterface]; ok {
+		s += " interface " + v.value.(string)
+	}
+	if v, ok := msg.Headers[FieldErrorName]; ok {
+		s += " error " + v.value.(string)
+	}
+	if v, ok := msg.Headers[FieldMember]; ok {
+		s += " member " + v.value.(string)
+	}
+	if len(msg.Body) != 0 {
+		s += "\n"
+	}
+	for i, v := range msg.Body {
+		s += "  " + MakeVariant(v).String()
+		if i != len(msg.Body)-1 {
+			s += "\n"
+		}
+	}
+	return s
+}
diff --git a/vendor/github.com/godbus/dbus/v5/object.go b/vendor/github.com/godbus/dbus/v5/object.go
new file mode 100644
index 000000000..8acd7fc8b
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/object.go
@@ -0,0 +1,211 @@
+package dbus
+
+import (
+	"context"
+	"errors"
+	"strings"
+)
+
+// BusObject is the interface of a remote object on which methods can be
+// invoked.
+type BusObject interface {
+	Call(method string, flags Flags, args ...interface{}) *Call
+	CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call
+	Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call
+	GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call
+	AddMatchSignal(iface, member string, options ...MatchOption) *Call
+	RemoveMatchSignal(iface, member string, options ...MatchOption) *Call
+	GetProperty(p string) (Variant, error)
+	SetProperty(p string, v interface{}) error
+	Destination() string
+	Path() ObjectPath
+}
+
+// Object represents a remote object on which methods can be invoked.
+type Object struct {
+	conn *Conn
+	dest string
+	path ObjectPath
+}
+
+// Call calls a method with (*Object).Go and waits for its reply.
+func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call {
+	return <-o.createCall(context.Background(), method, flags, make(chan *Call, 1), args...).Done
+}
+
+// CallWithContext acts like Call but takes a context
+func (o *Object) CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call {
+	return <-o.createCall(ctx, method, flags, make(chan *Call, 1), args...).Done
+}
+
+// AddMatchSignal subscribes BusObject to signals from specified interface,
+// method (member). Additional filter rules can be added via WithMatch* option constructors.
+// Note: To filter events by object path you have to specify this path via an option.
+//
+// Deprecated: use (*Conn) AddMatchSignal instead.
+func (o *Object) AddMatchSignal(iface, member string, options ...MatchOption) *Call {
+	base := []MatchOption{
+		withMatchType("signal"),
+		WithMatchInterface(iface),
+		WithMatchMember(member),
+	}
+
+	options = append(base, options...)
+	return o.conn.BusObject().Call(
+		"org.freedesktop.DBus.AddMatch",
+		0,
+		formatMatchOptions(options),
+	)
+}
+
+// RemoveMatchSignal unsubscribes BusObject from signals from specified interface,
+// method (member). Additional filter rules can be added via WithMatch* option constructors
+//
+// Deprecated: use (*Conn) RemoveMatchSignal instead.
+func (o *Object) RemoveMatchSignal(iface, member string, options ...MatchOption) *Call {
+	base := []MatchOption{
+		withMatchType("signal"),
+		WithMatchInterface(iface),
+		WithMatchMember(member),
+	}
+
+	options = append(base, options...)
+	return o.conn.BusObject().Call(
+		"org.freedesktop.DBus.RemoveMatch",
+		0,
+		formatMatchOptions(options),
+	)
+}
+
+// Go calls a method with the given arguments asynchronously. It returns a
+// Call structure representing this method call. The passed channel will
+// return the same value once the call is done. If ch is nil, a new channel
+// will be allocated. Otherwise, ch has to be buffered or Go will panic.
+//
+// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
+// is returned with any error in Err and a closed channel in Done containing
+// the returned Call as it's one entry.
+//
+// If the method parameter contains a dot ('.'), the part before the last dot
+// specifies the interface on which the method is called.
+func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
+	return o.createCall(context.Background(), method, flags, ch, args...)
+}
+
+// GoWithContext acts like Go but takes a context
+func (o *Object) GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
+	return o.createCall(ctx, method, flags, ch, args...)
+}
+
+func (o *Object) createCall(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
+	if ctx == nil {
+		panic("nil context")
+	}
+	iface := ""
+	i := strings.LastIndex(method, ".")
+	if i != -1 {
+		iface = method[:i]
+	}
+	method = method[i+1:]
+	msg := new(Message)
+	msg.Type = TypeMethodCall
+	msg.serial = o.conn.getSerial()
+	msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)
+	msg.Headers = make(map[HeaderField]Variant)
+	msg.Headers[FieldPath] = MakeVariant(o.path)
+	msg.Headers[FieldDestination] = MakeVariant(o.dest)
+	msg.Headers[FieldMember] = MakeVariant(method)
+	if iface != "" {
+		msg.Headers[FieldInterface] = MakeVariant(iface)
+	}
+	msg.Body = args
+	if len(args) > 0 {
+		msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...))
+	}
+	if msg.Flags&FlagNoReplyExpected == 0 {
+		if ch == nil {
+			ch = make(chan *Call, 1)
+		} else if cap(ch) == 0 {
+			panic("dbus: unbuffered channel passed to (*Object).Go")
+		}
+		ctx, cancel := context.WithCancel(ctx)
+		call := &Call{
+			Destination: o.dest,
+			Path:        o.path,
+			Method:      method,
+			Args:        args,
+			Done:        ch,
+			ctxCanceler: cancel,
+			ctx:         ctx,
+		}
+		o.conn.calls.track(msg.serial, call)
+		o.conn.sendMessageAndIfClosed(msg, func() {
+			o.conn.calls.handleSendError(msg, ErrClosed)
+			cancel()
+		})
+		go func() {
+			<-ctx.Done()
+			o.conn.calls.handleSendError(msg, ctx.Err())
+		}()
+
+		return call
+	}
+	done := make(chan *Call, 1)
+	call := &Call{
+		Err:  nil,
+		Done: done,
+	}
+	defer func() {
+		call.Done <- call
+		close(done)
+	}()
+	o.conn.sendMessageAndIfClosed(msg, func() {
+		call.Err = ErrClosed
+	})
+	return call
+}
+
+// GetProperty calls org.freedesktop.DBus.Properties.Get on the given
+// object. The property name must be given in interface.member notation.
+func (o *Object) GetProperty(p string) (Variant, error) {
+	idx := strings.LastIndex(p, ".")
+	if idx == -1 || idx+1 == len(p) {
+		return Variant{}, errors.New("dbus: invalid property " + p)
+	}
+
+	iface := p[:idx]
+	prop := p[idx+1:]
+
+	result := Variant{}
+	err := o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).Store(&result)
+
+	if err != nil {
+		return Variant{}, err
+	}
+
+	return result, nil
+}
+
+// SetProperty calls org.freedesktop.DBus.Properties.Set on the given
+// object. The property name must be given in interface.member notation.
+func (o *Object) SetProperty(p string, v interface{}) error {
+	idx := strings.LastIndex(p, ".")
+	if idx == -1 || idx+1 == len(p) {
+		return errors.New("dbus: invalid property " + p)
+	}
+
+	iface := p[:idx]
+	prop := p[idx+1:]
+
+	return o.Call("org.freedesktop.DBus.Properties.Set", 0, iface, prop, v).Err
+}
+
+// Destination returns the destination that calls on (o *Object) are sent to.
+func (o *Object) Destination() string {
+	return o.dest
+}
+
+// Path returns the path that calls on (o *Object") are sent to.
+func (o *Object) Path() ObjectPath {
+	return o.path
+}
diff --git a/vendor/github.com/godbus/dbus/v5/server_interfaces.go b/vendor/github.com/godbus/dbus/v5/server_interfaces.go
new file mode 100644
index 000000000..79d97edf3
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/server_interfaces.go
@@ -0,0 +1,107 @@
+package dbus
+
+// Terminator allows a handler to implement a shutdown mechanism that
+// is called when the connection terminates.
+type Terminator interface {
+	Terminate()
+}
+
+// Handler is the representation of a D-Bus Application.
+//
+// The Handler must have a way to lookup objects given
+// an ObjectPath. The returned object must implement the
+// ServerObject interface.
+type Handler interface {
+	LookupObject(path ObjectPath) (ServerObject, bool)
+}
+
+// ServerObject is the representation of an D-Bus Object.
+//
+// Objects are registered at a path for a given Handler.
+// The Objects implement D-Bus interfaces. The semantics
+// of Interface lookup is up to the implementation of
+// the ServerObject. The ServerObject implementation may
+// choose to implement empty string as a valid interface
+// represeting all methods or not per the D-Bus specification.
+type ServerObject interface {
+	LookupInterface(name string) (Interface, bool)
+}
+
+// An Interface is the representation of a D-Bus Interface.
+//
+// Interfaces are a grouping of methods implemented by the Objects.
+// Interfaces are responsible for routing method calls.
+type Interface interface {
+	LookupMethod(name string) (Method, bool)
+}
+
+// A Method represents the exposed methods on D-Bus.
+type Method interface {
+	// Call requires that all arguments are decoded before being passed to it.
+	Call(args ...interface{}) ([]interface{}, error)
+	NumArguments() int
+	NumReturns() int
+	// ArgumentValue returns a representative value for the argument at position
+	// it should be of the proper type. reflect.Zero would be a good mechanism
+	// to use for this Value.
+	ArgumentValue(position int) interface{}
+	// ReturnValue returns a representative value for the return at position
+	// it should be of the proper type. reflect.Zero would be a good mechanism
+	// to use for this Value.
+	ReturnValue(position int) interface{}
+}
+
+// An Argument Decoder can decode arguments using the non-standard mechanism
+//
+// If a method implements this interface then the non-standard
+// decoder will be used.
+//
+// Method arguments must be decoded from the message.
+// The mechanism for doing this will vary based on the
+// implementation of the method. A normal approach is provided
+// as part of this library, but may be replaced with
+// any other decoding scheme.
+type ArgumentDecoder interface {
+	// To decode the arguments of a method the sender and message are
+	// provided incase the semantics of the implementer provides access
+	// to these as part of the method invocation.
+	DecodeArguments(conn *Conn, sender string, msg *Message, args []interface{}) ([]interface{}, error)
+}
+
+// A SignalHandler is responsible for delivering a signal.
+//
+// Signal delivery may be changed from the default channel
+// based approach by Handlers implementing the SignalHandler
+// interface.
+type SignalHandler interface {
+	DeliverSignal(iface, name string, signal *Signal)
+}
+
+// SignalRegistrar manages signal delivery channels.
+//
+// This is an optional set of methods for `SignalHandler`.
+type SignalRegistrar interface {
+	AddSignal(ch chan<- *Signal)
+	RemoveSignal(ch chan<- *Signal)
+}
+
+// A DBusError is used to convert a generic object to a D-Bus error.
+//
+// Any custom error mechanism may implement this interface to provide
+// a custom encoding of the error on D-Bus. By default if a normal
+// error is returned, it will be encoded as the generic
+// "org.freedesktop.DBus.Error.Failed" error. By implementing this
+// interface as well a custom encoding may be provided.
+type DBusError interface {
+	DBusError() (string, []interface{})
+}
+
+// SerialGenerator is responsible for serials generation.
+//
+// Different approaches for the serial generation can be used,
+// maintaining a map guarded with a mutex (the standard way) or
+// simply increment an atomic counter.
+type SerialGenerator interface {
+	GetSerial() uint32
+	RetireSerial(serial uint32)
+}
diff --git a/vendor/github.com/godbus/dbus/v5/sig.go b/vendor/github.com/godbus/dbus/v5/sig.go
new file mode 100644
index 000000000..c1b809202
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/sig.go
@@ -0,0 +1,259 @@
+package dbus
+
+import (
+	"fmt"
+	"reflect"
+	"strings"
+)
+
+var sigToType = map[byte]reflect.Type{
+	'y': byteType,
+	'b': boolType,
+	'n': int16Type,
+	'q': uint16Type,
+	'i': int32Type,
+	'u': uint32Type,
+	'x': int64Type,
+	't': uint64Type,
+	'd': float64Type,
+	's': stringType,
+	'g': signatureType,
+	'o': objectPathType,
+	'v': variantType,
+	'h': unixFDIndexType,
+}
+
+// Signature represents a correct type signature as specified by the D-Bus
+// specification. The zero value represents the empty signature, "".
+type Signature struct {
+	str string
+}
+
+// SignatureOf returns the concatenation of all the signatures of the given
+// values. It panics if one of them is not representable in D-Bus.
+func SignatureOf(vs ...interface{}) Signature {
+	var s string
+	for _, v := range vs {
+		s += getSignature(reflect.TypeOf(v))
+	}
+	return Signature{s}
+}
+
+// SignatureOfType returns the signature of the given type. It panics if the
+// type is not representable in D-Bus.
+func SignatureOfType(t reflect.Type) Signature {
+	return Signature{getSignature(t)}
+}
+
+// getSignature returns the signature of the given type and panics on unknown types.
+func getSignature(t reflect.Type) string {
+	// handle simple types first
+	switch t.Kind() {
+	case reflect.Uint8:
+		return "y"
+	case reflect.Bool:
+		return "b"
+	case reflect.Int16:
+		return "n"
+	case reflect.Uint16:
+		return "q"
+	case reflect.Int, reflect.Int32:
+		if t == unixFDType {
+			return "h"
+		}
+		return "i"
+	case reflect.Uint, reflect.Uint32:
+		if t == unixFDIndexType {
+			return "h"
+		}
+		return "u"
+	case reflect.Int64:
+		return "x"
+	case reflect.Uint64:
+		return "t"
+	case reflect.Float64:
+		return "d"
+	case reflect.Ptr:
+		return getSignature(t.Elem())
+	case reflect.String:
+		if t == objectPathType {
+			return "o"
+		}
+		return "s"
+	case reflect.Struct:
+		if t == variantType {
+			return "v"
+		} else if t == signatureType {
+			return "g"
+		}
+		var s string
+		for i := 0; i < t.NumField(); i++ {
+			field := t.Field(i)
+			if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
+				s += getSignature(t.Field(i).Type)
+			}
+		}
+		return "(" + s + ")"
+	case reflect.Array, reflect.Slice:
+		return "a" + getSignature(t.Elem())
+	case reflect.Map:
+		if !isKeyType(t.Key()) {
+			panic(InvalidTypeError{t})
+		}
+		return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}"
+	case reflect.Interface:
+		return "v"
+	}
+	panic(InvalidTypeError{t})
+}
+
+// ParseSignature returns the signature represented by this string, or a
+// SignatureError if the string is not a valid signature.
+func ParseSignature(s string) (sig Signature, err error) {
+	if len(s) == 0 {
+		return
+	}
+	if len(s) > 255 {
+		return Signature{""}, SignatureError{s, "too long"}
+	}
+	sig.str = s
+	for err == nil && len(s) != 0 {
+		err, s = validSingle(s, 0)
+	}
+	if err != nil {
+		sig = Signature{""}
+	}
+
+	return
+}
+
+// ParseSignatureMust behaves like ParseSignature, except that it panics if s
+// is not valid.
+func ParseSignatureMust(s string) Signature {
+	sig, err := ParseSignature(s)
+	if err != nil {
+		panic(err)
+	}
+	return sig
+}
+
+// Empty retruns whether the signature is the empty signature.
+func (s Signature) Empty() bool {
+	return s.str == ""
+}
+
+// Single returns whether the signature represents a single, complete type.
+func (s Signature) Single() bool {
+	err, r := validSingle(s.str, 0)
+	return err != nil && r == ""
+}
+
+// String returns the signature's string representation.
+func (s Signature) String() string {
+	return s.str
+}
+
+// A SignatureError indicates that a signature passed to a function or received
+// on a connection is not a valid signature.
+type SignatureError struct {
+	Sig    string
+	Reason string
+}
+
+func (e SignatureError) Error() string {
+	return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason)
+}
+
+// Try to read a single type from this string. If it was successful, err is nil
+// and rem is the remaining unparsed part. Otherwise, err is a non-nil
+// SignatureError and rem is "". depth is the current recursion depth which may
+// not be greater than 64 and should be given as 0 on the first call.
+func validSingle(s string, depth int) (err error, rem string) {
+	if s == "" {
+		return SignatureError{Sig: s, Reason: "empty signature"}, ""
+	}
+	if depth > 64 {
+		return SignatureError{Sig: s, Reason: "container nesting too deep"}, ""
+	}
+	switch s[0] {
+	case 'y', 'b', 'n', 'q', 'i', 'u', 'x', 't', 'd', 's', 'g', 'o', 'v', 'h':
+		return nil, s[1:]
+	case 'a':
+		if len(s) > 1 && s[1] == '{' {
+			i := findMatching(s[1:], '{', '}')
+			if i == -1 {
+				return SignatureError{Sig: s, Reason: "unmatched '{'"}, ""
+			}
+			i++
+			rem = s[i+1:]
+			s = s[2:i]
+			if err, _ = validSingle(s[:1], depth+1); err != nil {
+				return err, ""
+			}
+			err, nr := validSingle(s[1:], depth+1)
+			if err != nil {
+				return err, ""
+			}
+			if nr != "" {
+				return SignatureError{Sig: s, Reason: "too many types in dict"}, ""
+			}
+			return nil, rem
+		}
+		return validSingle(s[1:], depth+1)
+	case '(':
+		i := findMatching(s, '(', ')')
+		if i == -1 {
+			return SignatureError{Sig: s, Reason: "unmatched ')'"}, ""
+		}
+		rem = s[i+1:]
+		s = s[1:i]
+		for err == nil && s != "" {
+			err, s = validSingle(s, depth+1)
+		}
+		if err != nil {
+			rem = ""
+		}
+		return
+	}
+	return SignatureError{Sig: s, Reason: "invalid type character"}, ""
+}
+
+func findMatching(s string, left, right rune) int {
+	n := 0
+	for i, v := range s {
+		if v == left {
+			n++
+		} else if v == right {
+			n--
+		}
+		if n == 0 {
+			return i
+		}
+	}
+	return -1
+}
+
+// typeFor returns the type of the given signature. It ignores any left over
+// characters and panics if s doesn't start with a valid type signature.
+func typeFor(s string) (t reflect.Type) {
+	err, _ := validSingle(s, 0)
+	if err != nil {
+		panic(err)
+	}
+
+	if t, ok := sigToType[s[0]]; ok {
+		return t
+	}
+	switch s[0] {
+	case 'a':
+		if s[1] == '{' {
+			i := strings.LastIndex(s, "}")
+			t = reflect.MapOf(sigToType[s[2]], typeFor(s[3:i]))
+		} else {
+			t = reflect.SliceOf(typeFor(s[1:]))
+		}
+	case '(':
+		t = interfacesType
+	}
+	return
+}
diff --git a/vendor/github.com/godbus/dbus/v5/transport_darwin.go b/vendor/github.com/godbus/dbus/v5/transport_darwin.go
new file mode 100644
index 000000000..1bba0d6bf
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/transport_darwin.go
@@ -0,0 +1,6 @@
+package dbus
+
+func (t *unixTransport) SendNullByte() error {
+	_, err := t.Write([]byte{0})
+	return err
+}
diff --git a/vendor/github.com/godbus/dbus/v5/transport_generic.go b/vendor/github.com/godbus/dbus/v5/transport_generic.go
new file mode 100644
index 000000000..718a1ff02
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/transport_generic.go
@@ -0,0 +1,50 @@
+package dbus
+
+import (
+	"encoding/binary"
+	"errors"
+	"io"
+	"unsafe"
+)
+
+var nativeEndian binary.ByteOrder
+
+func detectEndianness() binary.ByteOrder {
+	var x uint32 = 0x01020304
+	if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
+		return binary.BigEndian
+	}
+	return binary.LittleEndian
+}
+
+func init() {
+	nativeEndian = detectEndianness()
+}
+
+type genericTransport struct {
+	io.ReadWriteCloser
+}
+
+func (t genericTransport) SendNullByte() error {
+	_, err := t.Write([]byte{0})
+	return err
+}
+
+func (t genericTransport) SupportsUnixFDs() bool {
+	return false
+}
+
+func (t genericTransport) EnableUnixFDs() {}
+
+func (t genericTransport) ReadMessage() (*Message, error) {
+	return DecodeMessage(t)
+}
+
+func (t genericTransport) SendMessage(msg *Message) error {
+	for _, v := range msg.Body {
+		if _, ok := v.(UnixFD); ok {
+			return errors.New("dbus: unix fd passing not enabled")
+		}
+	}
+	return msg.EncodeTo(t, nativeEndian)
+}
diff --git a/vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go b/vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go
new file mode 100644
index 000000000..697739efa
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go
@@ -0,0 +1,39 @@
+//+build !windows
+
+package dbus
+
+import (
+	"errors"
+	"io/ioutil"
+	"net"
+)
+
+func init() {
+	transports["nonce-tcp"] = newNonceTcpTransport
+}
+
+func newNonceTcpTransport(keys string) (transport, error) {
+	host := getKey(keys, "host")
+	port := getKey(keys, "port")
+	noncefile := getKey(keys, "noncefile")
+	if host == "" || port == "" || noncefile == "" {
+		return nil, errors.New("dbus: unsupported address (must set host, port and noncefile)")
+	}
+	protocol, err := tcpFamily(keys)
+	if err != nil {
+		return nil, err
+	}
+	socket, err := net.Dial(protocol, net.JoinHostPort(host, port))
+	if err != nil {
+		return nil, err
+	}
+	b, err := ioutil.ReadFile(noncefile)
+	if err != nil {
+		return nil, err
+	}
+	_, err = socket.Write(b)
+	if err != nil {
+		return nil, err
+	}
+	return NewConn(socket)
+}
diff --git a/vendor/github.com/godbus/dbus/v5/transport_tcp.go b/vendor/github.com/godbus/dbus/v5/transport_tcp.go
new file mode 100644
index 000000000..f91c9b7d7
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/transport_tcp.go
@@ -0,0 +1,41 @@
+package dbus
+
+import (
+	"errors"
+	"net"
+)
+
+func init() {
+	transports["tcp"] = newTcpTransport
+}
+
+func tcpFamily(keys string) (string, error) {
+	switch getKey(keys, "family") {
+	case "":
+		return "tcp", nil
+	case "ipv4":
+		return "tcp4", nil
+	case "ipv6":
+		return "tcp6", nil
+	default:
+		return "", errors.New("dbus: invalid tcp family (must be ipv4 or ipv6)")
+	}
+}
+
+func newTcpTransport(keys string) (transport, error) {
+	host := getKey(keys, "host")
+	port := getKey(keys, "port")
+	if host == "" || port == "" {
+		return nil, errors.New("dbus: unsupported address (must set host and port)")
+	}
+
+	protocol, err := tcpFamily(keys)
+	if err != nil {
+		return nil, err
+	}
+	socket, err := net.Dial(protocol, net.JoinHostPort(host, port))
+	if err != nil {
+		return nil, err
+	}
+	return NewConn(socket)
+}
diff --git a/vendor/github.com/godbus/dbus/v5/transport_unix.go b/vendor/github.com/godbus/dbus/v5/transport_unix.go
new file mode 100644
index 000000000..c7cd02f97
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/transport_unix.go
@@ -0,0 +1,214 @@
+//+build !windows,!solaris
+
+package dbus
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"io"
+	"net"
+	"syscall"
+)
+
+type oobReader struct {
+	conn *net.UnixConn
+	oob  []byte
+	buf  [4096]byte
+}
+
+func (o *oobReader) Read(b []byte) (n int, err error) {
+	n, oobn, flags, _, err := o.conn.ReadMsgUnix(b, o.buf[:])
+	if err != nil {
+		return n, err
+	}
+	if flags&syscall.MSG_CTRUNC != 0 {
+		return n, errors.New("dbus: control data truncated (too many fds received)")
+	}
+	o.oob = append(o.oob, o.buf[:oobn]...)
+	return n, nil
+}
+
+type unixTransport struct {
+	*net.UnixConn
+	rdr        *oobReader
+	hasUnixFDs bool
+}
+
+func newUnixTransport(keys string) (transport, error) {
+	var err error
+
+	t := new(unixTransport)
+	abstract := getKey(keys, "abstract")
+	path := getKey(keys, "path")
+	switch {
+	case abstract == "" && path == "":
+		return nil, errors.New("dbus: invalid address (neither path nor abstract set)")
+	case abstract != "" && path == "":
+		t.UnixConn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: "@" + abstract, Net: "unix"})
+		if err != nil {
+			return nil, err
+		}
+		return t, nil
+	case abstract == "" && path != "":
+		t.UnixConn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: path, Net: "unix"})
+		if err != nil {
+			return nil, err
+		}
+		return t, nil
+	default:
+		return nil, errors.New("dbus: invalid address (both path and abstract set)")
+	}
+}
+
+func init() {
+	transports["unix"] = newUnixTransport
+}
+
+func (t *unixTransport) EnableUnixFDs() {
+	t.hasUnixFDs = true
+}
+
+func (t *unixTransport) ReadMessage() (*Message, error) {
+	var (
+		blen, hlen uint32
+		csheader   [16]byte
+		headers    []header
+		order      binary.ByteOrder
+		unixfds    uint32
+	)
+	// To be sure that all bytes of out-of-band data are read, we use a special
+	// reader that uses ReadUnix on the underlying connection instead of Read
+	// and gathers the out-of-band data in a buffer.
+	if t.rdr == nil {
+		t.rdr = &oobReader{conn: t.UnixConn}
+	} else {
+		t.rdr.oob = nil
+	}
+
+	// read the first 16 bytes (the part of the header that has a constant size),
+	// from which we can figure out the length of the rest of the message
+	if _, err := io.ReadFull(t.rdr, csheader[:]); err != nil {
+		return nil, err
+	}
+	switch csheader[0] {
+	case 'l':
+		order = binary.LittleEndian
+	case 'B':
+		order = binary.BigEndian
+	default:
+		return nil, InvalidMessageError("invalid byte order")
+	}
+	// csheader[4:8] -> length of message body, csheader[12:16] -> length of
+	// header fields (without alignment)
+	binary.Read(bytes.NewBuffer(csheader[4:8]), order, &blen)
+	binary.Read(bytes.NewBuffer(csheader[12:]), order, &hlen)
+	if hlen%8 != 0 {
+		hlen += 8 - (hlen % 8)
+	}
+
+	// decode headers and look for unix fds
+	headerdata := make([]byte, hlen+4)
+	copy(headerdata, csheader[12:])
+	if _, err := io.ReadFull(t.rdr, headerdata[4:]); err != nil {
+		return nil, err
+	}
+	dec := newDecoder(bytes.NewBuffer(headerdata), order)
+	dec.pos = 12
+	vs, err := dec.Decode(Signature{"a(yv)"})
+	if err != nil {
+		return nil, err
+	}
+	Store(vs, &headers)
+	for _, v := range headers {
+		if v.Field == byte(FieldUnixFDs) {
+			unixfds, _ = v.Variant.value.(uint32)
+		}
+	}
+	all := make([]byte, 16+hlen+blen)
+	copy(all, csheader[:])
+	copy(all[16:], headerdata[4:])
+	if _, err := io.ReadFull(t.rdr, all[16+hlen:]); err != nil {
+		return nil, err
+	}
+	if unixfds != 0 {
+		if !t.hasUnixFDs {
+			return nil, errors.New("dbus: got unix fds on unsupported transport")
+		}
+		// read the fds from the OOB data
+		scms, err := syscall.ParseSocketControlMessage(t.rdr.oob)
+		if err != nil {
+			return nil, err
+		}
+		if len(scms) != 1 {
+			return nil, errors.New("dbus: received more than one socket control message")
+		}
+		fds, err := syscall.ParseUnixRights(&scms[0])
+		if err != nil {
+			return nil, err
+		}
+		msg, err := DecodeMessage(bytes.NewBuffer(all))
+		if err != nil {
+			return nil, err
+		}
+		// substitute the values in the message body (which are indices for the
+		// array receiver via OOB) with the actual values
+		for i, v := range msg.Body {
+			switch v.(type) {
+			case UnixFDIndex:
+				j := v.(UnixFDIndex)
+				if uint32(j) >= unixfds {
+					return nil, InvalidMessageError("invalid index for unix fd")
+				}
+				msg.Body[i] = UnixFD(fds[j])
+			case []UnixFDIndex:
+				idxArray := v.([]UnixFDIndex)
+				fdArray := make([]UnixFD, len(idxArray))
+				for k, j := range idxArray {
+					if uint32(j) >= unixfds {
+						return nil, InvalidMessageError("invalid index for unix fd")
+					}
+					fdArray[k] = UnixFD(fds[j])
+				}
+				msg.Body[i] = fdArray
+			}
+		}
+		return msg, nil
+	}
+	return DecodeMessage(bytes.NewBuffer(all))
+}
+
+func (t *unixTransport) SendMessage(msg *Message) error {
+	fds := make([]int, 0)
+	for i, v := range msg.Body {
+		if fd, ok := v.(UnixFD); ok {
+			msg.Body[i] = UnixFDIndex(len(fds))
+			fds = append(fds, int(fd))
+		}
+	}
+	if len(fds) != 0 {
+		if !t.hasUnixFDs {
+			return errors.New("dbus: unix fd passing not enabled")
+		}
+		msg.Headers[FieldUnixFDs] = MakeVariant(uint32(len(fds)))
+		oob := syscall.UnixRights(fds...)
+		buf := new(bytes.Buffer)
+		msg.EncodeTo(buf, nativeEndian)
+		n, oobn, err := t.UnixConn.WriteMsgUnix(buf.Bytes(), oob, nil)
+		if err != nil {
+			return err
+		}
+		if n != buf.Len() || oobn != len(oob) {
+			return io.ErrShortWrite
+		}
+	} else {
+		if err := msg.EncodeTo(t, nativeEndian); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (t *unixTransport) SupportsUnixFDs() bool {
+	return true
+}
diff --git a/vendor/github.com/godbus/dbus/v5/transport_unixcred_dragonfly.go b/vendor/github.com/godbus/dbus/v5/transport_unixcred_dragonfly.go
new file mode 100644
index 000000000..a8cd39395
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/transport_unixcred_dragonfly.go
@@ -0,0 +1,95 @@
+// The UnixCredentials system call is currently only implemented on Linux
+// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
+// https://golang.org/s/go1.4-syscall
+// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
+
+// Local implementation of the UnixCredentials system call for DragonFly BSD
+
+package dbus
+
+/*
+#include <sys/ucred.h>
+*/
+import "C"
+
+import (
+	"io"
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+// http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go
+// http://golang.org/src/pkg/syscall/ztypes_dragonfly_amd64.go
+type Ucred struct {
+	Pid int32
+	Uid uint32
+	Gid uint32
+}
+
+// http://golang.org/src/pkg/syscall/types_linux.go
+// http://golang.org/src/pkg/syscall/types_dragonfly.go
+// https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/sys/sys/ucred.h
+const (
+	SizeofUcred = C.sizeof_struct_ucred
+)
+
+// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
+func cmsgAlignOf(salen int) int {
+	// From http://golang.org/src/pkg/syscall/sockcmsg_unix.go
+	//salign := sizeofPtr
+	// NOTE: It seems like 64-bit Darwin and DragonFly BSD kernels
+	// still require 32-bit aligned access to network subsystem.
+	//if darwin64Bit || dragonfly64Bit {
+	//	salign = 4
+	//}
+	salign := 4
+	return (salen + salign - 1) & ^(salign - 1)
+}
+
+// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
+func cmsgData(h *syscall.Cmsghdr) unsafe.Pointer {
+	return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(syscall.SizeofCmsghdr)))
+}
+
+// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
+// UnixCredentials encodes credentials into a socket control message
+// for sending to another process. This can be used for
+// authentication.
+func UnixCredentials(ucred *Ucred) []byte {
+	b := make([]byte, syscall.CmsgSpace(SizeofUcred))
+	h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+	h.Level = syscall.SOL_SOCKET
+	h.Type = syscall.SCM_CREDS
+	h.SetLen(syscall.CmsgLen(SizeofUcred))
+	*((*Ucred)(cmsgData(h))) = *ucred
+	return b
+}
+
+// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
+// ParseUnixCredentials decodes a socket control message that contains
+// credentials in a Ucred structure. To receive such a message, the
+// SO_PASSCRED option must be enabled on the socket.
+func ParseUnixCredentials(m *syscall.SocketControlMessage) (*Ucred, error) {
+	if m.Header.Level != syscall.SOL_SOCKET {
+		return nil, syscall.EINVAL
+	}
+	if m.Header.Type != syscall.SCM_CREDS {
+		return nil, syscall.EINVAL
+	}
+	ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
+	return &ucred, nil
+}
+
+func (t *unixTransport) SendNullByte() error {
+	ucred := &Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
+	b := UnixCredentials(ucred)
+	_, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil)
+	if err != nil {
+		return err
+	}
+	if oobn != len(b) {
+		return io.ErrShortWrite
+	}
+	return nil
+}
diff --git a/vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go b/vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go
new file mode 100644
index 000000000..0fc5b9273
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go
@@ -0,0 +1,91 @@
+// The UnixCredentials system call is currently only implemented on Linux
+// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
+// https://golang.org/s/go1.4-syscall
+// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
+
+// Local implementation of the UnixCredentials system call for FreeBSD
+
+package dbus
+
+/*
+const int sizeofPtr = sizeof(void*);
+#define _WANT_UCRED
+#include <sys/ucred.h>
+*/
+import "C"
+
+import (
+	"io"
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+// http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go
+// https://golang.org/src/syscall/ztypes_freebsd_amd64.go
+type Ucred struct {
+	Pid int32
+	Uid uint32
+	Gid uint32
+}
+
+// http://golang.org/src/pkg/syscall/types_linux.go
+// https://golang.org/src/syscall/types_freebsd.go
+// https://github.com/freebsd/freebsd/blob/master/sys/sys/ucred.h
+const (
+	SizeofUcred = C.sizeof_struct_ucred
+)
+
+// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
+func cmsgAlignOf(salen int) int {
+	salign := C.sizeofPtr
+
+	return (salen + salign - 1) & ^(salign - 1)
+}
+
+// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
+func cmsgData(h *syscall.Cmsghdr) unsafe.Pointer {
+	return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(syscall.SizeofCmsghdr)))
+}
+
+// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
+// UnixCredentials encodes credentials into a socket control message
+// for sending to another process. This can be used for
+// authentication.
+func UnixCredentials(ucred *Ucred) []byte {
+	b := make([]byte, syscall.CmsgSpace(SizeofUcred))
+	h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+	h.Level = syscall.SOL_SOCKET
+	h.Type = syscall.SCM_CREDS
+	h.SetLen(syscall.CmsgLen(SizeofUcred))
+	*((*Ucred)(cmsgData(h))) = *ucred
+	return b
+}
+
+// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
+// ParseUnixCredentials decodes a socket control message that contains
+// credentials in a Ucred structure. To receive such a message, the
+// SO_PASSCRED option must be enabled on the socket.
+func ParseUnixCredentials(m *syscall.SocketControlMessage) (*Ucred, error) {
+	if m.Header.Level != syscall.SOL_SOCKET {
+		return nil, syscall.EINVAL
+	}
+	if m.Header.Type != syscall.SCM_CREDS {
+		return nil, syscall.EINVAL
+	}
+	ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
+	return &ucred, nil
+}
+
+func (t *unixTransport) SendNullByte() error {
+	ucred := &Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
+	b := UnixCredentials(ucred)
+	_, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil)
+	if err != nil {
+		return err
+	}
+	if oobn != len(b) {
+		return io.ErrShortWrite
+	}
+	return nil
+}
diff --git a/vendor/github.com/godbus/dbus/v5/transport_unixcred_linux.go b/vendor/github.com/godbus/dbus/v5/transport_unixcred_linux.go
new file mode 100644
index 000000000..d9dfdf698
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/transport_unixcred_linux.go
@@ -0,0 +1,25 @@
+// The UnixCredentials system call is currently only implemented on Linux
+// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
+// https://golang.org/s/go1.4-syscall
+// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
+
+package dbus
+
+import (
+	"io"
+	"os"
+	"syscall"
+)
+
+func (t *unixTransport) SendNullByte() error {
+	ucred := &syscall.Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
+	b := syscall.UnixCredentials(ucred)
+	_, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil)
+	if err != nil {
+		return err
+	}
+	if oobn != len(b) {
+		return io.ErrShortWrite
+	}
+	return nil
+}
diff --git a/vendor/github.com/godbus/dbus/v5/transport_unixcred_openbsd.go b/vendor/github.com/godbus/dbus/v5/transport_unixcred_openbsd.go
new file mode 100644
index 000000000..af7bafdf9
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/transport_unixcred_openbsd.go
@@ -0,0 +1,14 @@
+package dbus
+
+import "io"
+
+func (t *unixTransport) SendNullByte() error {
+	n, _, err := t.UnixConn.WriteMsgUnix([]byte{0}, nil, nil)
+	if err != nil {
+		return err
+	}
+	if n != 1 {
+		return io.ErrShortWrite
+	}
+	return nil
+}
diff --git a/vendor/github.com/godbus/dbus/v5/variant.go b/vendor/github.com/godbus/dbus/v5/variant.go
new file mode 100644
index 000000000..5b51828c8
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/variant.go
@@ -0,0 +1,144 @@
+package dbus
+
+import (
+	"bytes"
+	"fmt"
+	"reflect"
+	"sort"
+	"strconv"
+)
+
+// Variant represents the D-Bus variant type.
+type Variant struct {
+	sig   Signature
+	value interface{}
+}
+
+// MakeVariant converts the given value to a Variant. It panics if v cannot be
+// represented as a D-Bus type.
+func MakeVariant(v interface{}) Variant {
+	return MakeVariantWithSignature(v, SignatureOf(v))
+}
+
+// MakeVariantWithSignature converts the given value to a Variant.
+func MakeVariantWithSignature(v interface{}, s Signature) Variant {
+	return Variant{s, v}
+}
+
+// ParseVariant parses the given string as a variant as described at
+// https://developer.gnome.org/glib/stable/gvariant-text.html. If sig is not
+// empty, it is taken to be the expected signature for the variant.
+func ParseVariant(s string, sig Signature) (Variant, error) {
+	tokens := varLex(s)
+	p := &varParser{tokens: tokens}
+	n, err := varMakeNode(p)
+	if err != nil {
+		return Variant{}, err
+	}
+	if sig.str == "" {
+		sig, err = varInfer(n)
+		if err != nil {
+			return Variant{}, err
+		}
+	}
+	v, err := n.Value(sig)
+	if err != nil {
+		return Variant{}, err
+	}
+	return MakeVariant(v), nil
+}
+
+// format returns a formatted version of v and whether this string can be parsed
+// unambigously.
+func (v Variant) format() (string, bool) {
+	switch v.sig.str[0] {
+	case 'b', 'i':
+		return fmt.Sprint(v.value), true
+	case 'n', 'q', 'u', 'x', 't', 'd', 'h':
+		return fmt.Sprint(v.value), false
+	case 's':
+		return strconv.Quote(v.value.(string)), true
+	case 'o':
+		return strconv.Quote(string(v.value.(ObjectPath))), false
+	case 'g':
+		return strconv.Quote(v.value.(Signature).str), false
+	case 'v':
+		s, unamb := v.value.(Variant).format()
+		if !unamb {
+			return "<@" + v.value.(Variant).sig.str + " " + s + ">", true
+		}
+		return "<" + s + ">", true
+	case 'y':
+		return fmt.Sprintf("%#x", v.value.(byte)), false
+	}
+	rv := reflect.ValueOf(v.value)
+	switch rv.Kind() {
+	case reflect.Slice:
+		if rv.Len() == 0 {
+			return "[]", false
+		}
+		unamb := true
+		buf := bytes.NewBuffer([]byte("["))
+		for i := 0; i < rv.Len(); i++ {
+			// TODO: slooow
+			s, b := MakeVariant(rv.Index(i).Interface()).format()
+			unamb = unamb && b
+			buf.WriteString(s)
+			if i != rv.Len()-1 {
+				buf.WriteString(", ")
+			}
+		}
+		buf.WriteByte(']')
+		return buf.String(), unamb
+	case reflect.Map:
+		if rv.Len() == 0 {
+			return "{}", false
+		}
+		unamb := true
+		var buf bytes.Buffer
+		kvs := make([]string, rv.Len())
+		for i, k := range rv.MapKeys() {
+			s, b := MakeVariant(k.Interface()).format()
+			unamb = unamb && b
+			buf.Reset()
+			buf.WriteString(s)
+			buf.WriteString(": ")
+			s, b = MakeVariant(rv.MapIndex(k).Interface()).format()
+			unamb = unamb && b
+			buf.WriteString(s)
+			kvs[i] = buf.String()
+		}
+		buf.Reset()
+		buf.WriteByte('{')
+		sort.Strings(kvs)
+		for i, kv := range kvs {
+			if i > 0 {
+				buf.WriteString(", ")
+			}
+			buf.WriteString(kv)
+		}
+		buf.WriteByte('}')
+		return buf.String(), unamb
+	}
+	return `"INVALID"`, true
+}
+
+// Signature returns the D-Bus signature of the underlying value of v.
+func (v Variant) Signature() Signature {
+	return v.sig
+}
+
+// String returns the string representation of the underlying value of v as
+// described at https://developer.gnome.org/glib/stable/gvariant-text.html.
+func (v Variant) String() string {
+	s, unamb := v.format()
+	if !unamb {
+		return "@" + v.sig.str + " " + s
+	}
+	return s
+}
+
+// Value returns the underlying value of v.
+func (v Variant) Value() interface{} {
+	return v.value
+}
diff --git a/vendor/github.com/godbus/dbus/v5/variant_lexer.go b/vendor/github.com/godbus/dbus/v5/variant_lexer.go
new file mode 100644
index 000000000..bf1398c8f
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/variant_lexer.go
@@ -0,0 +1,284 @@
+package dbus
+
+import (
+	"fmt"
+	"strings"
+	"unicode"
+	"unicode/utf8"
+)
+
+// Heavily inspired by the lexer from text/template.
+
+type varToken struct {
+	typ varTokenType
+	val string
+}
+
+type varTokenType byte
+
+const (
+	tokEOF varTokenType = iota
+	tokError
+	tokNumber
+	tokString
+	tokBool
+	tokArrayStart
+	tokArrayEnd
+	tokDictStart
+	tokDictEnd
+	tokVariantStart
+	tokVariantEnd
+	tokComma
+	tokColon
+	tokType
+	tokByteString
+)
+
+type varLexer struct {
+	input  string
+	start  int
+	pos    int
+	width  int
+	tokens []varToken
+}
+
+type lexState func(*varLexer) lexState
+
+func varLex(s string) []varToken {
+	l := &varLexer{input: s}
+	l.run()
+	return l.tokens
+}
+
+func (l *varLexer) accept(valid string) bool {
+	if strings.ContainsRune(valid, l.next()) {
+		return true
+	}
+	l.backup()
+	return false
+}
+
+func (l *varLexer) backup() {
+	l.pos -= l.width
+}
+
+func (l *varLexer) emit(t varTokenType) {
+	l.tokens = append(l.tokens, varToken{t, l.input[l.start:l.pos]})
+	l.start = l.pos
+}
+
+func (l *varLexer) errorf(format string, v ...interface{}) lexState {
+	l.tokens = append(l.tokens, varToken{
+		tokError,
+		fmt.Sprintf(format, v...),
+	})
+	return nil
+}
+
+func (l *varLexer) ignore() {
+	l.start = l.pos
+}
+
+func (l *varLexer) next() rune {
+	var r rune
+
+	if l.pos >= len(l.input) {
+		l.width = 0
+		return -1
+	}
+	r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
+	l.pos += l.width
+	return r
+}
+
+func (l *varLexer) run() {
+	for state := varLexNormal; state != nil; {
+		state = state(l)
+	}
+}
+
+func (l *varLexer) peek() rune {
+	r := l.next()
+	l.backup()
+	return r
+}
+
+func varLexNormal(l *varLexer) lexState {
+	for {
+		r := l.next()
+		switch {
+		case r == -1:
+			l.emit(tokEOF)
+			return nil
+		case r == '[':
+			l.emit(tokArrayStart)
+		case r == ']':
+			l.emit(tokArrayEnd)
+		case r == '{':
+			l.emit(tokDictStart)
+		case r == '}':
+			l.emit(tokDictEnd)
+		case r == '<':
+			l.emit(tokVariantStart)
+		case r == '>':
+			l.emit(tokVariantEnd)
+		case r == ':':
+			l.emit(tokColon)
+		case r == ',':
+			l.emit(tokComma)
+		case r == '\'' || r == '"':
+			l.backup()
+			return varLexString
+		case r == '@':
+			l.backup()
+			return varLexType
+		case unicode.IsSpace(r):
+			l.ignore()
+		case unicode.IsNumber(r) || r == '+' || r == '-':
+			l.backup()
+			return varLexNumber
+		case r == 'b':
+			pos := l.start
+			if n := l.peek(); n == '"' || n == '\'' {
+				return varLexByteString
+			}
+			// not a byte string; try to parse it as a type or bool below
+			l.pos = pos + 1
+			l.width = 1
+			fallthrough
+		default:
+			// either a bool or a type. Try bools first.
+			l.backup()
+			if l.pos+4 <= len(l.input) {
+				if l.input[l.pos:l.pos+4] == "true" {
+					l.pos += 4
+					l.emit(tokBool)
+					continue
+				}
+			}
+			if l.pos+5 <= len(l.input) {
+				if l.input[l.pos:l.pos+5] == "false" {
+					l.pos += 5
+					l.emit(tokBool)
+					continue
+				}
+			}
+			// must be a type.
+			return varLexType
+		}
+	}
+}
+
+var varTypeMap = map[string]string{
+	"boolean":    "b",
+	"byte":       "y",
+	"int16":      "n",
+	"uint16":     "q",
+	"int32":      "i",
+	"uint32":     "u",
+	"int64":      "x",
+	"uint64":     "t",
+	"double":     "f",
+	"string":     "s",
+	"objectpath": "o",
+	"signature":  "g",
+}
+
+func varLexByteString(l *varLexer) lexState {
+	q := l.next()
+Loop:
+	for {
+		switch l.next() {
+		case '\\':
+			if r := l.next(); r != -1 {
+				break
+			}
+			fallthrough
+		case -1:
+			return l.errorf("unterminated bytestring")
+		case q:
+			break Loop
+		}
+	}
+	l.emit(tokByteString)
+	return varLexNormal
+}
+
+func varLexNumber(l *varLexer) lexState {
+	l.accept("+-")
+	digits := "0123456789"
+	if l.accept("0") {
+		if l.accept("x") {
+			digits = "0123456789abcdefABCDEF"
+		} else {
+			digits = "01234567"
+		}
+	}
+	for strings.ContainsRune(digits, l.next()) {
+	}
+	l.backup()
+	if l.accept(".") {
+		for strings.ContainsRune(digits, l.next()) {
+		}
+		l.backup()
+	}
+	if l.accept("eE") {
+		l.accept("+-")
+		for strings.ContainsRune("0123456789", l.next()) {
+		}
+		l.backup()
+	}
+	if r := l.peek(); unicode.IsLetter(r) {
+		l.next()
+		return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
+	}
+	l.emit(tokNumber)
+	return varLexNormal
+}
+
+func varLexString(l *varLexer) lexState {
+	q := l.next()
+Loop:
+	for {
+		switch l.next() {
+		case '\\':
+			if r := l.next(); r != -1 {
+				break
+			}
+			fallthrough
+		case -1:
+			return l.errorf("unterminated string")
+		case q:
+			break Loop
+		}
+	}
+	l.emit(tokString)
+	return varLexNormal
+}
+
+func varLexType(l *varLexer) lexState {
+	at := l.accept("@")
+	for {
+		r := l.next()
+		if r == -1 {
+			break
+		}
+		if unicode.IsSpace(r) {
+			l.backup()
+			break
+		}
+	}
+	if at {
+		if _, err := ParseSignature(l.input[l.start+1 : l.pos]); err != nil {
+			return l.errorf("%s", err)
+		}
+	} else {
+		if _, ok := varTypeMap[l.input[l.start:l.pos]]; ok {
+			l.emit(tokType)
+			return varLexNormal
+		}
+		return l.errorf("unrecognized type %q", l.input[l.start:l.pos])
+	}
+	l.emit(tokType)
+	return varLexNormal
+}
diff --git a/vendor/github.com/godbus/dbus/v5/variant_parser.go b/vendor/github.com/godbus/dbus/v5/variant_parser.go
new file mode 100644
index 000000000..d20f5da6d
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/variant_parser.go
@@ -0,0 +1,817 @@
+package dbus
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"io"
+	"reflect"
+	"strconv"
+	"strings"
+	"unicode/utf8"
+)
+
+type varParser struct {
+	tokens []varToken
+	i      int
+}
+
+func (p *varParser) backup() {
+	p.i--
+}
+
+func (p *varParser) next() varToken {
+	if p.i < len(p.tokens) {
+		t := p.tokens[p.i]
+		p.i++
+		return t
+	}
+	return varToken{typ: tokEOF}
+}
+
+type varNode interface {
+	Infer() (Signature, error)
+	String() string
+	Sigs() sigSet
+	Value(Signature) (interface{}, error)
+}
+
+func varMakeNode(p *varParser) (varNode, error) {
+	var sig Signature
+
+	for {
+		t := p.next()
+		switch t.typ {
+		case tokEOF:
+			return nil, io.ErrUnexpectedEOF
+		case tokError:
+			return nil, errors.New(t.val)
+		case tokNumber:
+			return varMakeNumNode(t, sig)
+		case tokString:
+			return varMakeStringNode(t, sig)
+		case tokBool:
+			if sig.str != "" && sig.str != "b" {
+				return nil, varTypeError{t.val, sig}
+			}
+			b, err := strconv.ParseBool(t.val)
+			if err != nil {
+				return nil, err
+			}
+			return boolNode(b), nil
+		case tokArrayStart:
+			return varMakeArrayNode(p, sig)
+		case tokVariantStart:
+			return varMakeVariantNode(p, sig)
+		case tokDictStart:
+			return varMakeDictNode(p, sig)
+		case tokType:
+			if sig.str != "" {
+				return nil, errors.New("unexpected type annotation")
+			}
+			if t.val[0] == '@' {
+				sig.str = t.val[1:]
+			} else {
+				sig.str = varTypeMap[t.val]
+			}
+		case tokByteString:
+			if sig.str != "" && sig.str != "ay" {
+				return nil, varTypeError{t.val, sig}
+			}
+			b, err := varParseByteString(t.val)
+			if err != nil {
+				return nil, err
+			}
+			return byteStringNode(b), nil
+		default:
+			return nil, fmt.Errorf("unexpected %q", t.val)
+		}
+	}
+}
+
+type varTypeError struct {
+	val string
+	sig Signature
+}
+
+func (e varTypeError) Error() string {
+	return fmt.Sprintf("dbus: can't parse %q as type %q", e.val, e.sig.str)
+}
+
+type sigSet map[Signature]bool
+
+func (s sigSet) Empty() bool {
+	return len(s) == 0
+}
+
+func (s sigSet) Intersect(s2 sigSet) sigSet {
+	r := make(sigSet)
+	for k := range s {
+		if s2[k] {
+			r[k] = true
+		}
+	}
+	return r
+}
+
+func (s sigSet) Single() (Signature, bool) {
+	if len(s) == 1 {
+		for k := range s {
+			return k, true
+		}
+	}
+	return Signature{}, false
+}
+
+func (s sigSet) ToArray() sigSet {
+	r := make(sigSet, len(s))
+	for k := range s {
+		r[Signature{"a" + k.str}] = true
+	}
+	return r
+}
+
+type numNode struct {
+	sig Signature
+	str string
+	val interface{}
+}
+
+var numSigSet = sigSet{
+	Signature{"y"}: true,
+	Signature{"n"}: true,
+	Signature{"q"}: true,
+	Signature{"i"}: true,
+	Signature{"u"}: true,
+	Signature{"x"}: true,
+	Signature{"t"}: true,
+	Signature{"d"}: true,
+}
+
+func (n numNode) Infer() (Signature, error) {
+	if strings.ContainsAny(n.str, ".e") {
+		return Signature{"d"}, nil
+	}
+	return Signature{"i"}, nil
+}
+
+func (n numNode) String() string {
+	return n.str
+}
+
+func (n numNode) Sigs() sigSet {
+	if n.sig.str != "" {
+		return sigSet{n.sig: true}
+	}
+	if strings.ContainsAny(n.str, ".e") {
+		return sigSet{Signature{"d"}: true}
+	}
+	return numSigSet
+}
+
+func (n numNode) Value(sig Signature) (interface{}, error) {
+	if n.sig.str != "" && n.sig != sig {
+		return nil, varTypeError{n.str, sig}
+	}
+	if n.val != nil {
+		return n.val, nil
+	}
+	return varNumAs(n.str, sig)
+}
+
+func varMakeNumNode(tok varToken, sig Signature) (varNode, error) {
+	if sig.str == "" {
+		return numNode{str: tok.val}, nil
+	}
+	num, err := varNumAs(tok.val, sig)
+	if err != nil {
+		return nil, err
+	}
+	return numNode{sig: sig, val: num}, nil
+}
+
+func varNumAs(s string, sig Signature) (interface{}, error) {
+	isUnsigned := false
+	size := 32
+	switch sig.str {
+	case "n":
+		size = 16
+	case "i":
+	case "x":
+		size = 64
+	case "y":
+		size = 8
+		isUnsigned = true
+	case "q":
+		size = 16
+		isUnsigned = true
+	case "u":
+		isUnsigned = true
+	case "t":
+		size = 64
+		isUnsigned = true
+	case "d":
+		d, err := strconv.ParseFloat(s, 64)
+		if err != nil {
+			return nil, err
+		}
+		return d, nil
+	default:
+		return nil, varTypeError{s, sig}
+	}
+	base := 10
+	if strings.HasPrefix(s, "0x") {
+		base = 16
+		s = s[2:]
+	}
+	if strings.HasPrefix(s, "0") && len(s) != 1 {
+		base = 8
+		s = s[1:]
+	}
+	if isUnsigned {
+		i, err := strconv.ParseUint(s, base, size)
+		if err != nil {
+			return nil, err
+		}
+		var v interface{} = i
+		switch sig.str {
+		case "y":
+			v = byte(i)
+		case "q":
+			v = uint16(i)
+		case "u":
+			v = uint32(i)
+		}
+		return v, nil
+	}
+	i, err := strconv.ParseInt(s, base, size)
+	if err != nil {
+		return nil, err
+	}
+	var v interface{} = i
+	switch sig.str {
+	case "n":
+		v = int16(i)
+	case "i":
+		v = int32(i)
+	}
+	return v, nil
+}
+
+type stringNode struct {
+	sig Signature
+	str string      // parsed
+	val interface{} // has correct type
+}
+
+var stringSigSet = sigSet{
+	Signature{"s"}: true,
+	Signature{"g"}: true,
+	Signature{"o"}: true,
+}
+
+func (n stringNode) Infer() (Signature, error) {
+	return Signature{"s"}, nil
+}
+
+func (n stringNode) String() string {
+	return n.str
+}
+
+func (n stringNode) Sigs() sigSet {
+	if n.sig.str != "" {
+		return sigSet{n.sig: true}
+	}
+	return stringSigSet
+}
+
+func (n stringNode) Value(sig Signature) (interface{}, error) {
+	if n.sig.str != "" && n.sig != sig {
+		return nil, varTypeError{n.str, sig}
+	}
+	if n.val != nil {
+		return n.val, nil
+	}
+	switch {
+	case sig.str == "g":
+		return Signature{n.str}, nil
+	case sig.str == "o":
+		return ObjectPath(n.str), nil
+	case sig.str == "s":
+		return n.str, nil
+	default:
+		return nil, varTypeError{n.str, sig}
+	}
+}
+
+func varMakeStringNode(tok varToken, sig Signature) (varNode, error) {
+	if sig.str != "" && sig.str != "s" && sig.str != "g" && sig.str != "o" {
+		return nil, fmt.Errorf("invalid type %q for string", sig.str)
+	}
+	s, err := varParseString(tok.val)
+	if err != nil {
+		return nil, err
+	}
+	n := stringNode{str: s}
+	if sig.str == "" {
+		return stringNode{str: s}, nil
+	}
+	n.sig = sig
+	switch sig.str {
+	case "o":
+		n.val = ObjectPath(s)
+	case "g":
+		n.val = Signature{s}
+	case "s":
+		n.val = s
+	}
+	return n, nil
+}
+
+func varParseString(s string) (string, error) {
+	// quotes are guaranteed to be there
+	s = s[1 : len(s)-1]
+	buf := new(bytes.Buffer)
+	for len(s) != 0 {
+		r, size := utf8.DecodeRuneInString(s)
+		if r == utf8.RuneError && size == 1 {
+			return "", errors.New("invalid UTF-8")
+		}
+		s = s[size:]
+		if r != '\\' {
+			buf.WriteRune(r)
+			continue
+		}
+		r, size = utf8.DecodeRuneInString(s)
+		if r == utf8.RuneError && size == 1 {
+			return "", errors.New("invalid UTF-8")
+		}
+		s = s[size:]
+		switch r {
+		case 'a':
+			buf.WriteRune(0x7)
+		case 'b':
+			buf.WriteRune(0x8)
+		case 'f':
+			buf.WriteRune(0xc)
+		case 'n':
+			buf.WriteRune('\n')
+		case 'r':
+			buf.WriteRune('\r')
+		case 't':
+			buf.WriteRune('\t')
+		case '\n':
+		case 'u':
+			if len(s) < 4 {
+				return "", errors.New("short unicode escape")
+			}
+			r, err := strconv.ParseUint(s[:4], 16, 32)
+			if err != nil {
+				return "", err
+			}
+			buf.WriteRune(rune(r))
+			s = s[4:]
+		case 'U':
+			if len(s) < 8 {
+				return "", errors.New("short unicode escape")
+			}
+			r, err := strconv.ParseUint(s[:8], 16, 32)
+			if err != nil {
+				return "", err
+			}
+			buf.WriteRune(rune(r))
+			s = s[8:]
+		default:
+			buf.WriteRune(r)
+		}
+	}
+	return buf.String(), nil
+}
+
+var boolSigSet = sigSet{Signature{"b"}: true}
+
+type boolNode bool
+
+func (boolNode) Infer() (Signature, error) {
+	return Signature{"b"}, nil
+}
+
+func (b boolNode) String() string {
+	if b {
+		return "true"
+	}
+	return "false"
+}
+
+func (boolNode) Sigs() sigSet {
+	return boolSigSet
+}
+
+func (b boolNode) Value(sig Signature) (interface{}, error) {
+	if sig.str != "b" {
+		return nil, varTypeError{b.String(), sig}
+	}
+	return bool(b), nil
+}
+
+type arrayNode struct {
+	set      sigSet
+	children []varNode
+	val      interface{}
+}
+
+func (n arrayNode) Infer() (Signature, error) {
+	for _, v := range n.children {
+		csig, err := varInfer(v)
+		if err != nil {
+			continue
+		}
+		return Signature{"a" + csig.str}, nil
+	}
+	return Signature{}, fmt.Errorf("can't infer type for %q", n.String())
+}
+
+func (n arrayNode) String() string {
+	s := "["
+	for i, v := range n.children {
+		s += v.String()
+		if i != len(n.children)-1 {
+			s += ", "
+		}
+	}
+	return s + "]"
+}
+
+func (n arrayNode) Sigs() sigSet {
+	return n.set
+}
+
+func (n arrayNode) Value(sig Signature) (interface{}, error) {
+	if n.set.Empty() {
+		// no type information whatsoever, so this must be an empty slice
+		return reflect.MakeSlice(typeFor(sig.str), 0, 0).Interface(), nil
+	}
+	if !n.set[sig] {
+		return nil, varTypeError{n.String(), sig}
+	}
+	s := reflect.MakeSlice(typeFor(sig.str), len(n.children), len(n.children))
+	for i, v := range n.children {
+		rv, err := v.Value(Signature{sig.str[1:]})
+		if err != nil {
+			return nil, err
+		}
+		s.Index(i).Set(reflect.ValueOf(rv))
+	}
+	return s.Interface(), nil
+}
+
+func varMakeArrayNode(p *varParser, sig Signature) (varNode, error) {
+	var n arrayNode
+	if sig.str != "" {
+		n.set = sigSet{sig: true}
+	}
+	if t := p.next(); t.typ == tokArrayEnd {
+		return n, nil
+	} else {
+		p.backup()
+	}
+Loop:
+	for {
+		t := p.next()
+		switch t.typ {
+		case tokEOF:
+			return nil, io.ErrUnexpectedEOF
+		case tokError:
+			return nil, errors.New(t.val)
+		}
+		p.backup()
+		cn, err := varMakeNode(p)
+		if err != nil {
+			return nil, err
+		}
+		if cset := cn.Sigs(); !cset.Empty() {
+			if n.set.Empty() {
+				n.set = cset.ToArray()
+			} else {
+				nset := cset.ToArray().Intersect(n.set)
+				if nset.Empty() {
+					return nil, fmt.Errorf("can't parse %q with given type information", cn.String())
+				}
+				n.set = nset
+			}
+		}
+		n.children = append(n.children, cn)
+		switch t := p.next(); t.typ {
+		case tokEOF:
+			return nil, io.ErrUnexpectedEOF
+		case tokError:
+			return nil, errors.New(t.val)
+		case tokArrayEnd:
+			break Loop
+		case tokComma:
+			continue
+		default:
+			return nil, fmt.Errorf("unexpected %q", t.val)
+		}
+	}
+	return n, nil
+}
+
+type variantNode struct {
+	n varNode
+}
+
+var variantSet = sigSet{
+	Signature{"v"}: true,
+}
+
+func (variantNode) Infer() (Signature, error) {
+	return Signature{"v"}, nil
+}
+
+func (n variantNode) String() string {
+	return "<" + n.n.String() + ">"
+}
+
+func (variantNode) Sigs() sigSet {
+	return variantSet
+}
+
+func (n variantNode) Value(sig Signature) (interface{}, error) {
+	if sig.str != "v" {
+		return nil, varTypeError{n.String(), sig}
+	}
+	sig, err := varInfer(n.n)
+	if err != nil {
+		return nil, err
+	}
+	v, err := n.n.Value(sig)
+	if err != nil {
+		return nil, err
+	}
+	return MakeVariant(v), nil
+}
+
+func varMakeVariantNode(p *varParser, sig Signature) (varNode, error) {
+	n, err := varMakeNode(p)
+	if err != nil {
+		return nil, err
+	}
+	if t := p.next(); t.typ != tokVariantEnd {
+		return nil, fmt.Errorf("unexpected %q", t.val)
+	}
+	vn := variantNode{n}
+	if sig.str != "" && sig.str != "v" {
+		return nil, varTypeError{vn.String(), sig}
+	}
+	return variantNode{n}, nil
+}
+
+type dictEntry struct {
+	key, val varNode
+}
+
+type dictNode struct {
+	kset, vset sigSet
+	children   []dictEntry
+	val        interface{}
+}
+
+func (n dictNode) Infer() (Signature, error) {
+	for _, v := range n.children {
+		ksig, err := varInfer(v.key)
+		if err != nil {
+			continue
+		}
+		vsig, err := varInfer(v.val)
+		if err != nil {
+			continue
+		}
+		return Signature{"a{" + ksig.str + vsig.str + "}"}, nil
+	}
+	return Signature{}, fmt.Errorf("can't infer type for %q", n.String())
+}
+
+func (n dictNode) String() string {
+	s := "{"
+	for i, v := range n.children {
+		s += v.key.String() + ": " + v.val.String()
+		if i != len(n.children)-1 {
+			s += ", "
+		}
+	}
+	return s + "}"
+}
+
+func (n dictNode) Sigs() sigSet {
+	r := sigSet{}
+	for k := range n.kset {
+		for v := range n.vset {
+			sig := "a{" + k.str + v.str + "}"
+			r[Signature{sig}] = true
+		}
+	}
+	return r
+}
+
+func (n dictNode) Value(sig Signature) (interface{}, error) {
+	set := n.Sigs()
+	if set.Empty() {
+		// no type information -> empty dict
+		return reflect.MakeMap(typeFor(sig.str)).Interface(), nil
+	}
+	if !set[sig] {
+		return nil, varTypeError{n.String(), sig}
+	}
+	m := reflect.MakeMap(typeFor(sig.str))
+	ksig := Signature{sig.str[2:3]}
+	vsig := Signature{sig.str[3 : len(sig.str)-1]}
+	for _, v := range n.children {
+		kv, err := v.key.Value(ksig)
+		if err != nil {
+			return nil, err
+		}
+		vv, err := v.val.Value(vsig)
+		if err != nil {
+			return nil, err
+		}
+		m.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv))
+	}
+	return m.Interface(), nil
+}
+
+func varMakeDictNode(p *varParser, sig Signature) (varNode, error) {
+	var n dictNode
+
+	if sig.str != "" {
+		if len(sig.str) < 5 {
+			return nil, fmt.Errorf("invalid signature %q for dict type", sig)
+		}
+		ksig := Signature{string(sig.str[2])}
+		vsig := Signature{sig.str[3 : len(sig.str)-1]}
+		n.kset = sigSet{ksig: true}
+		n.vset = sigSet{vsig: true}
+	}
+	if t := p.next(); t.typ == tokDictEnd {
+		return n, nil
+	} else {
+		p.backup()
+	}
+Loop:
+	for {
+		t := p.next()
+		switch t.typ {
+		case tokEOF:
+			return nil, io.ErrUnexpectedEOF
+		case tokError:
+			return nil, errors.New(t.val)
+		}
+		p.backup()
+		kn, err := varMakeNode(p)
+		if err != nil {
+			return nil, err
+		}
+		if kset := kn.Sigs(); !kset.Empty() {
+			if n.kset.Empty() {
+				n.kset = kset
+			} else {
+				n.kset = kset.Intersect(n.kset)
+				if n.kset.Empty() {
+					return nil, fmt.Errorf("can't parse %q with given type information", kn.String())
+				}
+			}
+		}
+		t = p.next()
+		switch t.typ {
+		case tokEOF:
+			return nil, io.ErrUnexpectedEOF
+		case tokError:
+			return nil, errors.New(t.val)
+		case tokColon:
+		default:
+			return nil, fmt.Errorf("unexpected %q", t.val)
+		}
+		t = p.next()
+		switch t.typ {
+		case tokEOF:
+			return nil, io.ErrUnexpectedEOF
+		case tokError:
+			return nil, errors.New(t.val)
+		}
+		p.backup()
+		vn, err := varMakeNode(p)
+		if err != nil {
+			return nil, err
+		}
+		if vset := vn.Sigs(); !vset.Empty() {
+			if n.vset.Empty() {
+				n.vset = vset
+			} else {
+				n.vset = n.vset.Intersect(vset)
+				if n.vset.Empty() {
+					return nil, fmt.Errorf("can't parse %q with given type information", vn.String())
+				}
+			}
+		}
+		n.children = append(n.children, dictEntry{kn, vn})
+		t = p.next()
+		switch t.typ {
+		case tokEOF:
+			return nil, io.ErrUnexpectedEOF
+		case tokError:
+			return nil, errors.New(t.val)
+		case tokDictEnd:
+			break Loop
+		case tokComma:
+			continue
+		default:
+			return nil, fmt.Errorf("unexpected %q", t.val)
+		}
+	}
+	return n, nil
+}
+
+type byteStringNode []byte
+
+var byteStringSet = sigSet{
+	Signature{"ay"}: true,
+}
+
+func (byteStringNode) Infer() (Signature, error) {
+	return Signature{"ay"}, nil
+}
+
+func (b byteStringNode) String() string {
+	return string(b)
+}
+
+func (b byteStringNode) Sigs() sigSet {
+	return byteStringSet
+}
+
+func (b byteStringNode) Value(sig Signature) (interface{}, error) {
+	if sig.str != "ay" {
+		return nil, varTypeError{b.String(), sig}
+	}
+	return []byte(b), nil
+}
+
+func varParseByteString(s string) ([]byte, error) {
+	// quotes and b at start are guaranteed to be there
+	b := make([]byte, 0, 1)
+	s = s[2 : len(s)-1]
+	for len(s) != 0 {
+		c := s[0]
+		s = s[1:]
+		if c != '\\' {
+			b = append(b, c)
+			continue
+		}
+		c = s[0]
+		s = s[1:]
+		switch c {
+		case 'a':
+			b = append(b, 0x7)
+		case 'b':
+			b = append(b, 0x8)
+		case 'f':
+			b = append(b, 0xc)
+		case 'n':
+			b = append(b, '\n')
+		case 'r':
+			b = append(b, '\r')
+		case 't':
+			b = append(b, '\t')
+		case 'x':
+			if len(s) < 2 {
+				return nil, errors.New("short escape")
+			}
+			n, err := strconv.ParseUint(s[:2], 16, 8)
+			if err != nil {
+				return nil, err
+			}
+			b = append(b, byte(n))
+			s = s[2:]
+		case '0':
+			if len(s) < 3 {
+				return nil, errors.New("short escape")
+			}
+			n, err := strconv.ParseUint(s[:3], 8, 8)
+			if err != nil {
+				return nil, err
+			}
+			b = append(b, byte(n))
+			s = s[3:]
+		default:
+			b = append(b, c)
+		}
+	}
+	return append(b, 0), nil
+}
+
+func varInfer(n varNode) (Signature, error) {
+	if sig, ok := n.Sigs().Single(); ok {
+		return sig, nil
+	}
+	return n.Infer()
+}
diff --git a/vendor/github.com/zalando/go-keyring/.catwatch.yml b/vendor/github.com/zalando/go-keyring/.catwatch.yml
new file mode 100644
index 000000000..7167a18ac
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/.catwatch.yml
@@ -0,0 +1,1 @@
+title: go-keyring
diff --git a/vendor/github.com/zalando/go-keyring/.zappr.yml b/vendor/github.com/zalando/go-keyring/.zappr.yml
new file mode 100644
index 000000000..736c105e5
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/.zappr.yml
@@ -0,0 +1,8 @@
+approvals:
+  groups:
+    zalando:
+      minimum: 2
+      from:
+        orgs:
+          - "zalando"
+X-Zalando-Team: teapot
diff --git a/vendor/github.com/zalando/go-keyring/CONTRIBUTING.md b/vendor/github.com/zalando/go-keyring/CONTRIBUTING.md
new file mode 100644
index 000000000..317952e68
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/CONTRIBUTING.md
@@ -0,0 +1,12 @@
+# Contributing to Go keyring library
+
+Please open an issue for bugs and feature requests. We are open to Pull Requests, but we would like to discuss features with you.
+If you want to submit a PR, always use feature branches and let people discuss changes in pull requests.
+
+Pull requests should only be merged after all discussions have been concluded and at least 2 reviewers has given their
+**approval**.
+
+## Guidelines
+
+- **every code change** should have a test
+- keep the current code style
diff --git a/vendor/github.com/zalando/go-keyring/LICENSE b/vendor/github.com/zalando/go-keyring/LICENSE
new file mode 100644
index 000000000..1c494f92f
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Zalando SE
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/zalando/go-keyring/MAINTAINERS b/vendor/github.com/zalando/go-keyring/MAINTAINERS
new file mode 100644
index 000000000..755e7e3b1
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/MAINTAINERS
@@ -0,0 +1,2 @@
+Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
+Sandor Szücs <sandor.szuecs@zalando.de>
diff --git a/vendor/github.com/zalando/go-keyring/README.md b/vendor/github.com/zalando/go-keyring/README.md
new file mode 100644
index 000000000..e6b43d08d
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/README.md
@@ -0,0 +1,146 @@
+# Go Keyring library
+[![Build Status](https://travis-ci.org/zalando/go-keyring.svg?branch=master)](https://travis-ci.org/zalando/go-keyring)
+[![Build status](https://ci.appveyor.com/api/projects/status/l8hdbqng769sc2c5/branch/master?svg=true)](https://ci.appveyor.com/project/mikkeloscar/go-keyring/branch/master)
+[![Go Report Card](https://goreportcard.com/badge/github.com/zalando/go-keyring)](https://goreportcard.com/report/github.com/zalando/go-keyring)
+[![GoDoc](https://godoc.org/github.com/zalando/go-keyring?status.svg)](https://godoc.org/github.com/zalando/go-keyring)
+
+`go-keyring` is an OS-agnostic library for *setting*, *getting* and *deleting*
+secrets from the system keyring. It supports **OS X**, **Linux (dbus)** and
+**Windows**.
+
+go-keyring was created after its authors searched for, but couldn't find, a better alternative. It aims to simplify
+using statically linked binaries, which is cumbersome when relying on C bindings (as other keyring libraries do).
+
+#### Potential Uses
+
+If you're working with an application that needs to store user credentials
+locally on the user's machine, go-keyring might come in handy. For instance, if you are writing a CLI for an API
+that requires a username and password, you can store this information in the
+keyring instead of having the user type it on every invocation.
+
+## Dependencies
+
+#### OS X
+
+The OS X implementation depends on the `/usr/bin/security` binary for
+interfacing with the OS X keychain. It should be available by default.
+
+#### Linux
+
+The Linux implementation depends on the [Secret Service][SecretService] dbus
+interface, which is provided by [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring).
+
+It's expected that the default collection `login` exists in the keyring, because
+it's the default in most distros. If it doesn't exist, you can create it through the
+keyring frontend program [Seahorse](https://wiki.gnome.org/Apps/Seahorse):
+
+ * Open `seahorse`
+ * Go to **File > New > Password Keyring**
+ * Click **Continue**
+ * When asked for a name, use: **login**
+
+## Example Usage
+
+How to *set* and *get* a secret from the keyring:
+
+```go
+package main
+
+import (
+    "log"
+
+    "github.com/zalando/go-keyring"
+)
+
+func main() {
+    service := "my-app"
+    user := "anon"
+    password := "secret"
+
+    // set password
+    err := keyring.Set(service, user, password)
+    if err != nil {
+        log.Fatal(err)
+    }
+
+    // get password
+    secret, err := keyring.Get(service, user)
+    if err != nil {
+        log.Fatal(err)
+    }
+
+    log.Println(secret)
+}
+
+```
+
+## Tests
+### Running tests
+Running the tests is simple:
+
+```
+go test
+```
+
+Which OS you use *does* matter. If you're using **Linux**, it will
+test the implementation in `keyring_linux.go`. If running the tests
+on **OS X**, it will test the implementation in `keyring_darwin.go`.
+
+### Mocking
+If you need to mock the keyring behavior for testing on systems without a keyring implementation you can call `MockInit()` which will replace the OS defined provider with an in-memory one.
+
+```go
+package implementation
+
+import (
+    "testing"
+
+    "github.com/zalando/go-keyring"
+)
+
+func TestMockedSetGet(t *testing.T) {
+    keyring.MockInit()
+    err := keyring.Set("service", "user", "password")
+    if err != nil {
+        t.Fatal(err)
+    }
+
+    p, err := keyring.Get("service", "user")
+    if err != nil {
+        t.Fatal(err)
+    }
+
+    if p != "password" {
+        t.Error("password was not the expected string")
+    }
+
+}
+
+```
+
+## Contributing/TODO
+
+We welcome contributions from the community; please use [CONTRIBUTING.md](CONTRIBUTING.md) as your guidelines for getting started. Here are some items that we'd love help with:
+
+- The code base
+- Better test coverage
+
+Please use GitHub issues as the starting point for contributions, new ideas and/or bug reports.
+
+## Contact
+
+* E-Mail: team-teapot@zalando.de
+* Security issues: Please send an email to the [maintainers](MAINTAINERS), and we'll try to get back to you within two workdays. If you don't hear back, send an email to team-teapot@zalando.de and someone will respond within five days max.
+
+## Contributors
+
+Thanks to:
+
+- [your name here]
+
+## License
+
+See [LICENSE](LICENSE) file.
+
+
+[SecretService]: https://specifications.freedesktop.org/secret-service/latest/
diff --git a/vendor/github.com/zalando/go-keyring/SECURITY.md b/vendor/github.com/zalando/go-keyring/SECURITY.md
new file mode 100644
index 000000000..2d5b2dde6
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/SECURITY.md
@@ -0,0 +1,8 @@
+We acknowledge that every line of code that we write may potentially contain security issues.
+We are trying to deal with it responsibly and provide patches as quickly as possible.
+
+We host our bug bounty program on HackerOne, it is currently private, therefore if you would like to report a vulnerability and get rewarded for it, please ask to join our program by filling this form:
+
+https://corporate.zalando.com/en/services-and-contact#security-form
+
+You can also send your report via this form if you do not want to join our bug bounty program and just want to report a vulnerability or security issue.
diff --git a/vendor/github.com/zalando/go-keyring/appveyor.yml b/vendor/github.com/zalando/go-keyring/appveyor.yml
new file mode 100644
index 000000000..5ff01024d
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/appveyor.yml
@@ -0,0 +1,11 @@
+version: "{build}"
+clone_folder: c:\gopath\src\github.com\zalando\go-keyring
+environment:
+  GOPATH: c:\gopath
+install:
+  - go version
+  - go get -t -v ./...
+build_script:
+  - go build
+test_script:
+  - go test -v
diff --git a/vendor/github.com/zalando/go-keyring/go.mod b/vendor/github.com/zalando/go-keyring/go.mod
new file mode 100644
index 000000000..29416adeb
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/go.mod
@@ -0,0 +1,8 @@
+module github.com/zalando/go-keyring
+
+go 1.13
+
+require (
+	github.com/danieljoos/wincred v1.1.0
+	github.com/godbus/dbus/v5 v5.0.3
+)
diff --git a/vendor/github.com/zalando/go-keyring/go.sum b/vendor/github.com/zalando/go-keyring/go.sum
new file mode 100644
index 000000000..7023136a3
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/go.sum
@@ -0,0 +1,16 @@
+github.com/danieljoos/wincred v1.1.0 h1:3RNcEpBg4IhIChZdFRSdlQt1QjCp1sMAPIrOnm7Yf8g=
+github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
+github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/vendor/github.com/zalando/go-keyring/keyring.go b/vendor/github.com/zalando/go-keyring/keyring.go
new file mode 100644
index 000000000..0b94ad562
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/keyring.go
@@ -0,0 +1,38 @@
+package keyring
+
+import "fmt"
+
+// provider set in the init function by the relevant os file e.g.:
+// keyring_linux.go
+var provider Keyring = fallbackServiceProvider{}
+
+var (
+	// ErrNotFound is the expected error if the secret isn't found in the
+	// keyring.
+	ErrNotFound = fmt.Errorf("secret not found in keyring")
+)
+
+// Keyring provides a simple set/get interface for a keyring service.
+type Keyring interface {
+	// Set password in keyring for user.
+	Set(service, user, password string) error
+	// Get password from keyring given service and user name.
+	Get(service, user string) (string, error)
+	// Delete secret from keyring.
+	Delete(service, user string) error
+}
+
+// Set password in keyring for user.
+func Set(service, user, password string) error {
+	return provider.Set(service, user, password)
+}
+
+// Get password from keyring given service and user name.
+func Get(service, user string) (string, error) {
+	return provider.Get(service, user)
+}
+
+// Delete secret from keyring.
+func Delete(service, user string) error {
+	return provider.Delete(service, user)
+}
diff --git a/vendor/github.com/zalando/go-keyring/keyring_darwin.go b/vendor/github.com/zalando/go-keyring/keyring_darwin.go
new file mode 100644
index 000000000..91569d836
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/keyring_darwin.go
@@ -0,0 +1,94 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package keyring
+
+import (
+	"encoding/hex"
+	"fmt"
+	"os/exec"
+	"strings"
+)
+
+const (
+	execPathKeychain = "/usr/bin/security"
+
+	// encodingPrefix is a well-known prefix added to strings encoded by Set.
+	encodingPrefix = "go-keyring-encoded:"
+)
+
+type macOSXKeychain struct{}
+
+// func (*MacOSXKeychain) IsAvailable() bool {
+// 	return exec.Command(execPathKeychain).Run() != exec.ErrNotFound
+// }
+
+// Set stores stores user and pass in the keyring under the defined service
+// name.
+func (k macOSXKeychain) Get(service, username string) (string, error) {
+	out, err := exec.Command(
+		execPathKeychain,
+		"find-generic-password",
+		"-s", service,
+		"-wa", username).CombinedOutput()
+	if err != nil {
+		if strings.Contains(fmt.Sprintf("%s", out), "could not be found") {
+			err = ErrNotFound
+		}
+		return "", err
+	}
+
+	trimStr := strings.TrimSpace(string(out[:]))
+	// if the string has the well-known prefix, assume it's encoded
+	if strings.HasPrefix(trimStr, encodingPrefix) {
+		dec, err := hex.DecodeString(trimStr[len(encodingPrefix):])
+		return string(dec), err
+	}
+
+	return trimStr, nil
+}
+
+// Set stores a secret in the keyring given a service name and a user.
+func (k macOSXKeychain) Set(service, username, password string) error {
+	// if the added secret has multiple lines, osx will hex encode it
+	// identify this with a well-known prefix.
+	if strings.ContainsRune(password, '\n') {
+		password = encodingPrefix + hex.EncodeToString([]byte(password))
+	}
+
+	return exec.Command(
+		execPathKeychain,
+		"add-generic-password",
+		"-U", //update if exists
+		"-s", service,
+		"-a", username,
+		"-w", password).Run()
+}
+
+// Delete deletes a secret, identified by service & user, from the keyring.
+func (k macOSXKeychain) Delete(service, username string) error {
+	out, err := exec.Command(
+		execPathKeychain,
+		"delete-generic-password",
+		"-s", service,
+		"-a", username).CombinedOutput()
+	if strings.Contains(fmt.Sprintf("%s", out), "could not be found") {
+		err = ErrNotFound
+	}
+	return err
+}
+
+func init() {
+	provider = macOSXKeychain{}
+}
diff --git a/vendor/github.com/zalando/go-keyring/keyring_fallback.go b/vendor/github.com/zalando/go-keyring/keyring_fallback.go
new file mode 100644
index 000000000..cda7f6a30
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/keyring_fallback.go
@@ -0,0 +1,23 @@
+package keyring
+
+import (
+	"errors"
+	"runtime"
+)
+
+// All of the following methods error out on unsupported platforms
+var ErrUnsupportedPlatform = errors.New("Unsupported platform: " + runtime.GOOS)
+
+type fallbackServiceProvider struct{}
+
+func (fallbackServiceProvider) Set(service, user, pass string) error {
+	return ErrUnsupportedPlatform
+}
+
+func (fallbackServiceProvider) Get(service, user string) (string, error) {
+	return "", ErrUnsupportedPlatform
+}
+
+func (fallbackServiceProvider) Delete(service, user string) error {
+	return ErrUnsupportedPlatform
+}
diff --git a/vendor/github.com/zalando/go-keyring/keyring_linux.go b/vendor/github.com/zalando/go-keyring/keyring_linux.go
new file mode 100644
index 000000000..9d65533ff
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/keyring_linux.go
@@ -0,0 +1,120 @@
+package keyring
+
+import (
+	"fmt"
+	dbus "github.com/godbus/dbus/v5"
+	"github.com/zalando/go-keyring/secret_service"
+)
+
+type secretServiceProvider struct{}
+
+// Set stores user and pass in the keyring under the defined service
+// name.
+func (s secretServiceProvider) Set(service, user, pass string) error {
+	svc, err := ss.NewSecretService()
+	if err != nil {
+		return err
+	}
+
+	// open a session
+	session, err := svc.OpenSession()
+	if err != nil {
+		return err
+	}
+	defer svc.Close(session)
+
+	attributes := map[string]string{
+		"username": user,
+		"service":  service,
+	}
+
+	secret := ss.NewSecret(session.Path(), pass)
+
+	collection := svc.GetLoginCollection()
+
+	err = svc.Unlock(collection.Path())
+	if err != nil {
+		return err
+	}
+
+	err = svc.CreateItem(collection,
+		fmt.Sprintf("Password for '%s' on '%s'", user, service),
+		attributes, secret)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// findItem looksup an item by service and user.
+func (s secretServiceProvider) findItem(svc *ss.SecretService, service, user string) (dbus.ObjectPath, error) {
+	collection := svc.GetLoginCollection()
+
+	search := map[string]string{
+		"username": user,
+		"service":  service,
+	}
+
+	err := svc.Unlock(collection.Path())
+	if err != nil {
+		return "", err
+	}
+
+	results, err := svc.SearchItems(collection, search)
+	if err != nil {
+		return "", err
+	}
+
+	if len(results) == 0 {
+		return "", ErrNotFound
+	}
+
+	return results[0], nil
+}
+
+// Get gets a secret from the keyring given a service name and a user.
+func (s secretServiceProvider) Get(service, user string) (string, error) {
+	svc, err := ss.NewSecretService()
+	if err != nil {
+		return "", err
+	}
+
+	item, err := s.findItem(svc, service, user)
+	if err != nil {
+		return "", err
+	}
+
+	// open a session
+	session, err := svc.OpenSession()
+	if err != nil {
+		return "", err
+	}
+	defer svc.Close(session)
+
+	secret, err := svc.GetSecret(item, session.Path())
+	if err != nil {
+		return "", err
+	}
+
+	return string(secret.Value), nil
+}
+
+// Delete deletes a secret, identified by service & user, from the keyring.
+func (s secretServiceProvider) Delete(service, user string) error {
+	svc, err := ss.NewSecretService()
+	if err != nil {
+		return err
+	}
+
+	item, err := s.findItem(svc, service, user)
+	if err != nil {
+		return err
+	}
+
+	return svc.Delete(item)
+}
+
+func init() {
+	provider = secretServiceProvider{}
+}
diff --git a/vendor/github.com/zalando/go-keyring/keyring_mock.go b/vendor/github.com/zalando/go-keyring/keyring_mock.go
new file mode 100644
index 000000000..693f8cf23
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/keyring_mock.go
@@ -0,0 +1,46 @@
+package keyring
+
+type mockProvider struct {
+	mockStore map[string]map[string]string
+}
+
+// Set stores user and pass in the keyring under the defined service
+// name.
+func (m *mockProvider) Set(service, user, pass string) error {
+	if m.mockStore == nil {
+		m.mockStore = make(map[string]map[string]string)
+	}
+	if m.mockStore[service] == nil {
+		m.mockStore[service] = make(map[string]string)
+	}
+	m.mockStore[service][user] = pass
+	return nil
+}
+
+// Get gets a secret from the keyring given a service name and a user.
+func (m *mockProvider) Get(service, user string) (string, error) {
+	if b, ok := m.mockStore[service]; ok {
+		if v, ok := b[user]; ok {
+			return v, nil
+		}
+	}
+	return "", ErrNotFound
+}
+
+// Delete deletes a secret, identified by service & user, from the keyring.
+func (m *mockProvider) Delete(service, user string) error {
+	if m.mockStore != nil {
+		if _, ok := m.mockStore[service]; ok {
+			if _, ok := m.mockStore[service][user]; ok {
+				delete(m.mockStore[service], user)
+				return nil
+			}
+		}
+	}
+	return ErrNotFound
+}
+
+// MockInit sets the provider to a mocked memory store
+func MockInit() {
+	provider = &mockProvider{}
+}
diff --git a/vendor/github.com/zalando/go-keyring/keyring_windows.go b/vendor/github.com/zalando/go-keyring/keyring_windows.go
new file mode 100644
index 000000000..85345d5bf
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/keyring_windows.go
@@ -0,0 +1,52 @@
+package keyring
+
+import (
+	"github.com/danieljoos/wincred"
+	"syscall"
+)
+
+type windowsKeychain struct{}
+
+// Get gets a secret from the keyring given a service name and a user.
+func (k windowsKeychain) Get(service, username string) (string, error) {
+	cred, err := wincred.GetGenericCredential(k.credName(service, username))
+	if err != nil {
+		if err == syscall.ERROR_NOT_FOUND {
+			return "", ErrNotFound
+		}
+		return "", err
+	}
+
+	return string(cred.CredentialBlob), nil
+}
+
+// Set stores stores user and pass in the keyring under the defined service
+// name.
+func (k windowsKeychain) Set(service, username, password string) error {
+	cred := wincred.NewGenericCredential(k.credName(service, username))
+	cred.UserName = username
+	cred.CredentialBlob = []byte(password)
+	return cred.Write()
+}
+
+// Delete deletes a secret, identified by service & user, from the keyring.
+func (k windowsKeychain) Delete(service, username string) error {
+	cred, err := wincred.GetGenericCredential(k.credName(service, username))
+	if err != nil {
+		if err == syscall.ERROR_NOT_FOUND {
+			return ErrNotFound
+		}
+		return err
+	}
+
+	return cred.Delete()
+}
+
+// credName combines service and username to a single string.
+func (k windowsKeychain) credName(service, username string) string {
+	return service + ":" + username
+}
+
+func init() {
+	provider = windowsKeychain{}
+}
diff --git a/vendor/github.com/zalando/go-keyring/secret_service/secret_service.go b/vendor/github.com/zalando/go-keyring/secret_service/secret_service.go
new file mode 100644
index 000000000..e4644b1b5
--- /dev/null
+++ b/vendor/github.com/zalando/go-keyring/secret_service/secret_service.go
@@ -0,0 +1,245 @@
+package ss
+
+import (
+	"fmt"
+
+	"errors"
+	dbus "github.com/godbus/dbus/v5"
+)
+
+const (
+	serviceName          = "org.freedesktop.secrets"
+	servicePath          = "/org/freedesktop/secrets"
+	serviceInterface     = "org.freedesktop.Secret.Service"
+	collectionInterface  = "org.freedesktop.Secret.Collection"
+	collectionsInterface = "org.freedesktop.Secret.Service.Collections"
+	itemInterface        = "org.freedesktop.Secret.Item"
+	sessionInterface     = "org.freedesktop.Secret.Session"
+	promptInterface      = "org.freedesktop.Secret.Prompt"
+
+	loginCollectionAlias = "/org/freedesktop/secrets/aliases/default"
+	collectionBasePath   = "/org/freedesktop/secrets/collection/"
+)
+
+// Secret defines a org.freedesk.Secret.Item secret struct.
+type Secret struct {
+	Session     dbus.ObjectPath
+	Parameters  []byte
+	Value       []byte
+	ContentType string `dbus:"content_type"`
+}
+
+// NewSecret initializes a new Secret.
+func NewSecret(session dbus.ObjectPath, secret string) Secret {
+	return Secret{
+		Session:     session,
+		Parameters:  []byte{},
+		Value:       []byte(secret),
+		ContentType: "text/plain; charset=utf8",
+	}
+}
+
+// SecretService is an interface for the Secret Service dbus API.
+type SecretService struct {
+	*dbus.Conn
+	object dbus.BusObject
+}
+
+// NewSecretService inializes a new SecretService object.
+func NewSecretService() (*SecretService, error) {
+	conn, err := dbus.SessionBus()
+	if err != nil {
+		return nil, err
+	}
+
+	return &SecretService{
+		conn,
+		conn.Object(serviceName, servicePath),
+	}, nil
+}
+
+// OpenSession opens a secret service session.
+func (s *SecretService) OpenSession() (dbus.BusObject, error) {
+	var disregard dbus.Variant
+	var sessionPath dbus.ObjectPath
+	err := s.object.Call(serviceInterface+".OpenSession", 0, "plain", dbus.MakeVariant("")).Store(&disregard, &sessionPath)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.Object(serviceName, sessionPath), nil
+}
+
+// CheckCollectionPath accepts dbus path and returns nil if the path is found
+// in the collection interface (and can be used).
+func (s *SecretService) CheckCollectionPath(path dbus.ObjectPath) error {
+	obj := s.Conn.Object(serviceName, servicePath)
+	val, err := obj.GetProperty(collectionsInterface)
+	if err != nil {
+		return err
+	}
+	paths := val.Value().([]dbus.ObjectPath)
+	for _, p := range paths {
+		if p == path {
+			return nil
+		}
+	}
+	return errors.New("path not found")
+}
+
+// GetCollection returns a collection from a name.
+func (s *SecretService) GetCollection(name string) dbus.BusObject {
+	return s.Object(serviceName, dbus.ObjectPath(collectionBasePath+name))
+}
+
+// GetLoginCollection decides and returns the dbus collection to be used for login.
+func (s *SecretService) GetLoginCollection() dbus.BusObject {
+	path := dbus.ObjectPath(collectionBasePath + "login")
+	if err := s.CheckCollectionPath(path); err != nil {
+		path = dbus.ObjectPath(loginCollectionAlias)
+	}
+	return s.Object(serviceName, path)
+}
+
+// Unlock unlocks a collection.
+func (s *SecretService) Unlock(collection dbus.ObjectPath) error {
+	var unlocked []dbus.ObjectPath
+	var prompt dbus.ObjectPath
+	err := s.object.Call(serviceInterface+".Unlock", 0, []dbus.ObjectPath{collection}).Store(&unlocked, &prompt)
+	if err != nil {
+		return err
+	}
+
+	_, v, err := s.handlePrompt(prompt)
+	if err != nil {
+		return err
+	}
+
+	collections := v.Value()
+	switch c := collections.(type) {
+	case []dbus.ObjectPath:
+		unlocked = append(unlocked, c...)
+	}
+
+	if len(unlocked) != 1 || (collection != loginCollectionAlias && unlocked[0] != collection) {
+		return fmt.Errorf("failed to unlock correct collection '%v'", collection)
+	}
+
+	return nil
+}
+
+// Close closes a secret service dbus session.
+func (s *SecretService) Close(session dbus.BusObject) error {
+	return session.Call(sessionInterface+".Close", 0).Err
+}
+
+// CreateCollection with the supplied label.
+func (s *SecretService) CreateCollection(label string) (dbus.BusObject, error) {
+	properties := map[string]dbus.Variant{
+		collectionInterface + ".Label": dbus.MakeVariant(label),
+	}
+	var collection, prompt dbus.ObjectPath
+	err := s.object.Call(serviceInterface+".CreateCollection", 0, properties, "").
+		Store(&collection, &prompt)
+	if err != nil {
+		return nil, err
+	}
+
+	_, v, err := s.handlePrompt(prompt)
+	if err != nil {
+		return nil, err
+	}
+
+	if v.String() != "" {
+		collection = dbus.ObjectPath(v.String())
+	}
+
+	return s.Object(serviceName, collection), nil
+}
+
+// CreateItem creates an item in a collection, with label, attributes and a
+// related secret.
+func (s *SecretService) CreateItem(collection dbus.BusObject, label string, attributes map[string]string, secret Secret) error {
+	properties := map[string]dbus.Variant{
+		itemInterface + ".Label":      dbus.MakeVariant(label),
+		itemInterface + ".Attributes": dbus.MakeVariant(attributes),
+	}
+
+	var item, prompt dbus.ObjectPath
+	err := collection.Call(collectionInterface+".CreateItem", 0,
+		properties, secret, true).Store(&item, &prompt)
+	if err != nil {
+		return err
+	}
+
+	_, _, err = s.handlePrompt(prompt)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// handlePrompt checks if a prompt should be handles and handles it by
+// triggering the prompt and waiting for the Sercret service daemon to display
+// the prompt to the user.
+func (s *SecretService) handlePrompt(prompt dbus.ObjectPath) (bool, dbus.Variant, error) {
+	if prompt != dbus.ObjectPath("/") {
+		err := s.Object(serviceName, prompt).Call(promptInterface+".Prompt", 0, "").Err
+		if err != nil {
+			return false, dbus.MakeVariant(""), err
+		}
+
+		promptSignal := make(chan *dbus.Signal, 1)
+		s.Signal(promptSignal)
+
+		signal := <-promptSignal
+		switch signal.Name {
+		case promptInterface + ".Completed":
+			dismissed := signal.Body[0].(bool)
+			result := signal.Body[1].(dbus.Variant)
+			return dismissed, result, nil
+		}
+
+	}
+
+	return false, dbus.MakeVariant(""), nil
+}
+
+// SearchItems returns a list of items matching the search object.
+func (s *SecretService) SearchItems(collection dbus.BusObject, search interface{}) ([]dbus.ObjectPath, error) {
+	var results []dbus.ObjectPath
+	err := collection.Call(collectionInterface+".SearchItems", 0, search).Store(&results)
+	if err != nil {
+		return nil, err
+	}
+
+	return results, nil
+}
+
+// GetSecret gets secret from an item in a given session.
+func (s *SecretService) GetSecret(itemPath dbus.ObjectPath, session dbus.ObjectPath) (*Secret, error) {
+	var secret Secret
+	err := s.Object(serviceName, itemPath).Call(itemInterface+".GetSecret", 0, session).Store(&secret)
+	if err != nil {
+		return nil, err
+	}
+
+	return &secret, nil
+}
+
+// Delete deletes an item from the collection.
+func (s *SecretService) Delete(itemPath dbus.ObjectPath) error {
+	var prompt dbus.ObjectPath
+	err := s.Object(serviceName, itemPath).Call(itemInterface+".Delete", 0).Store(&prompt)
+	if err != nil {
+		return err
+	}
+
+	_, _, err = s.handlePrompt(prompt)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index c4b95fade..be80fd9f3 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -58,6 +58,8 @@ github.com/cucumber/godog/colors
 # github.com/cucumber/messages-go/v10 v10.0.3
 ## explicit
 github.com/cucumber/messages-go/v10
+# github.com/danieljoos/wincred v1.1.0
+github.com/danieljoos/wincred
 # github.com/davecgh/go-spew v1.1.1
 github.com/davecgh/go-spew/spew
 # github.com/docker/docker v1.13.1 => github.com/docker/docker v1.4.2-0.20191121165722-d1d5f6476656
@@ -77,6 +79,8 @@ github.com/fatih/color
 github.com/fsnotify/fsnotify
 # github.com/go-logr/logr v0.2.0
 github.com/go-logr/logr
+# github.com/godbus/dbus/v5 v5.0.3
+github.com/godbus/dbus/v5
 # github.com/gofrs/uuid v3.2.0+incompatible
 github.com/gofrs/uuid
 # github.com/gogo/protobuf v1.3.1
@@ -237,6 +241,10 @@ github.com/xi2/xz
 # github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c
 ## explicit
 github.com/xtgo/uuid
+# github.com/zalando/go-keyring v0.1.1
+## explicit
+github.com/zalando/go-keyring
+github.com/zalando/go-keyring/secret_service
 # golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c
 ## explicit
 golang.org/x/crypto/blowfish


Test patch:
diff --git a/pkg/crc/cluster/pullsecret_test.go b/pkg/crc/cluster/pullsecret_test.go
index 2d789de62..63cff8635 100644
--- a/pkg/crc/cluster/pullsecret_test.go
+++ b/pkg/crc/cluster/pullsecret_test.go
@@ -9,14 +9,18 @@ import (
 	cmdConfig "github.com/code-ready/crc/cmd/crc/cmd/config"
 	"github.com/code-ready/crc/pkg/crc/config"
 	"github.com/stretchr/testify/assert"
+	"github.com/zalando/go-keyring"
 )
 
 const (
 	secret1 = `{"auths":{"quay.io":{"auth":"secret1"}}}` // #nosec G101
 	secret2 = `{"auths":{"quay.io":{"auth":"secret2"}}}` // #nosec G101
+	secret3 = `{"auths":{"quay.io":{"auth":"secret3"}}}` // #nosec G101
 )
 
 func TestLoadPullSecret(t *testing.T) {
+	keyring.MockInit()
+
 	dir, err := ioutil.TempDir("", "pull-secret")
 	assert.NoError(t, err)
 	defer os.RemoveAll(dir)
@@ -32,11 +36,17 @@ func TestLoadPullSecret(t *testing.T) {
 	_, err = loader.Value()
 	assert.Error(t, err)
 
+	assert.NoError(t, storeInKeyring(secret3))
+
+	val, err := loader.Value()
+	assert.NoError(t, err)
+	assert.Equal(t, secret3, val)
+
 	assert.NoError(t, ioutil.WriteFile(filepath.Join(dir, "file2"), []byte(secret2), 0600))
 	_, err = cfg.Set(cmdConfig.PullSecretFile, filepath.Join(dir, "file2"))
 	assert.NoError(t, err)
 
-	val, err := loader.Value()
+	val, err = loader.Value()
 	assert.NoError(t, err)
 	assert.Equal(t, secret2, val)
 
diff --git a/pkg/crc/preflight/preflight_darwin_test.go b/pkg/crc/preflight/preflight_darwin_test.go
index 19bc178b1..a327cc38e 100644
--- a/pkg/crc/preflight/preflight_darwin_test.go
+++ b/pkg/crc/preflight/preflight_darwin_test.go
@@ -16,9 +16,9 @@ func TestCountConfigurationOptions(t *testing.T) {
 }
 
 func TestCountPreflights(t *testing.T) {
-	assert.Len(t, getPreflightChecks(false, network.DefaultMode), 13)
-	assert.Len(t, getPreflightChecks(true, network.DefaultMode), 19)
+	assert.Len(t, getPreflightChecks(false, network.DefaultMode), 14)
+	assert.Len(t, getPreflightChecks(true, network.DefaultMode), 20)
 
-	assert.Len(t, getPreflightChecks(false, network.VSockMode), 12)
-	assert.Len(t, getPreflightChecks(true, network.VSockMode), 18)
+	assert.Len(t, getPreflightChecks(false, network.VSockMode), 13)
+	assert.Len(t, getPreflightChecks(true, network.VSockMode), 19)
 }
diff --git a/pkg/crc/preflight/preflight_linux_test.go b/pkg/crc/preflight/preflight_linux_test.go
index 4e76233b8..020da1455 100644
--- a/pkg/crc/preflight/preflight_linux_test.go
+++ b/pkg/crc/preflight/preflight_linux_test.go
@@ -6,7 +6,7 @@ import (
 	"testing"
 
 	"github.com/code-ready/crc/pkg/crc/adminhelper"
-
+	"github.com/code-ready/crc/pkg/crc/cluster"
 	"github.com/code-ready/crc/pkg/crc/config"
 	"github.com/code-ready/crc/pkg/crc/network"
 	crcos "github.com/code-ready/crc/pkg/os/linux"
@@ -69,6 +69,7 @@ var checkListForDistros = []checkListForDistro{
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
+			{cleanup: cluster.ForgetPullSecret},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -99,6 +100,7 @@ var checkListForDistros = []checkListForDistro{
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
+			{cleanup: cluster.ForgetPullSecret},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -122,6 +124,7 @@ var checkListForDistros = []checkListForDistro{
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
+			{cleanup: cluster.ForgetPullSecret},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -151,6 +154,7 @@ var checkListForDistros = []checkListForDistro{
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
+			{cleanup: cluster.ForgetPullSecret},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -174,6 +178,7 @@ var checkListForDistros = []checkListForDistro{
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
+			{cleanup: cluster.ForgetPullSecret},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -203,6 +208,7 @@ var checkListForDistros = []checkListForDistro{
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
+			{cleanup: cluster.ForgetPullSecret},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -226,6 +232,7 @@ var checkListForDistros = []checkListForDistro{
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
+			{cleanup: cluster.ForgetPullSecret},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -257,6 +264,7 @@ var checkListForDistros = []checkListForDistro{
 			{configKeySuffix: "check-ram"},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
+			{cleanup: cluster.ForgetPullSecret},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
diff --git a/pkg/crc/preflight/preflight_windows_test.go b/pkg/crc/preflight/preflight_windows_test.go
index 37ba75340..0c3c4220c 100644
--- a/pkg/crc/preflight/preflight_windows_test.go
+++ b/pkg/crc/preflight/preflight_windows_test.go
@@ -15,9 +15,9 @@ func TestCountConfigurationOptions(t *testing.T) {
 }
 
 func TestCountPreflights(t *testing.T) {
-	assert.Len(t, getPreflightChecks(false, network.DefaultMode), 15)
-	assert.Len(t, getPreflightChecks(true, network.DefaultMode), 18)
+	assert.Len(t, getPreflightChecks(false, network.DefaultMode), 16)
+	assert.Len(t, getPreflightChecks(true, network.DefaultMode), 19)
 
-	assert.Len(t, getPreflightChecks(false, network.VSockMode), 16)
-	assert.Len(t, getPreflightChecks(true, network.VSockMode), 19)
+	assert.Len(t, getPreflightChecks(false, network.VSockMode), 17)
+	assert.Len(t, getPreflightChecks(true, network.VSockMode), 20)
 }
diff --git a/test/e2e/crcsuite/crcsuite.go b/test/e2e/crcsuite/crcsuite.go
index afd5b80fb..7336b7ac5 100644
--- a/test/e2e/crcsuite/crcsuite.go
+++ b/test/e2e/crcsuite/crcsuite.go
@@ -57,8 +57,8 @@ func FeatureContext(s *godog.Suite) {
 		CheckOutputMatchWithRetry)
 	s.Step(`^checking that CRC is (running|stopped)$`,
 		CheckCRCStatus)
-	s.Step(`stdout (?:should contain|contains) "(.*)" if bundle (is|is not) embedded$`,
-		StdoutContainsIfBundleEmbeddedOrNot)
+	s.Step(`^(stdout|stderr) (?:should contain|contains) "(.*)" if bundle (is|is not) embedded$`,
+		CommandReturnShouldContainIfBundleEmbeddedOrNot)
 
 	// CRC file operations
 	s.Step(`^file "([^"]*)" exists in CRC home folder$`,
@@ -415,18 +415,18 @@ func StartCRCWithDefaultBundleAndNameServerSucceedsOrFails(nameserver string, ex
 	return clicumber.ExecuteCommandSucceedsOrFails(cmd, expected)
 }
 
-func StdoutContainsIfBundleEmbeddedOrNot(value string, expected string) error {
+func CommandReturnShouldContainIfBundleEmbeddedOrNot(commandField string, value string, expected string) error {
 	if expected == "is" { // expect embedded
 		if bundleEmbedded { // really embedded
-			return clicumber.CommandReturnShouldContain("stdout", value)
+			return clicumber.CommandReturnShouldContain(commandField, value)
 		}
-		return clicumber.CommandReturnShouldNotContain("stdout", value)
+		return clicumber.CommandReturnShouldNotContain(commandField, value)
 	}
 	// expect not embedded
 	if !bundleEmbedded { // really not embedded
-		return clicumber.CommandReturnShouldContain("stdout", value)
+		return clicumber.CommandReturnShouldContain(commandField, value)
 	}
-	return clicumber.CommandReturnShouldNotContain("stdout", value)
+	return clicumber.CommandReturnShouldNotContain(commandField, value)
 }
 
 func SetConfigPropertyToValueSucceedsOrFails(property string, value string, expected string) error {
diff --git a/test/e2e/features/basic.feature b/test/e2e/features/basic.feature
index 822164f10..e2c794538 100644
--- a/test/e2e/features/basic.feature
+++ b/test/e2e/features/basic.feature
@@ -121,7 +121,7 @@ Feature: Basic test
         Then stdout should contain "To login as a regular user, run 'oc login -u developer -p developer"
         And stdout should contain "To login as an admin, run 'oc login -u kubeadmin -p "
 
-    @darwin @linux @windows
+    @darwin @linux
     Scenario: Monitoring stack check
         Given checking that CRC is running
         When executing "eval $(crc oc-env)" succeeds
@@ -131,6 +131,16 @@ Feature: Basic test
         And unsetting config property "enable-cluster-monitoring" succeeds
         And unsetting config property "memory" succeeds
 
+    @windows
+    Scenario: Monitoring stack check
+        Given checking that CRC is running
+        And executing "crc oc-env | Invoke-Expression" succeeds
+        And login to the oc cluster succeeds
+        And executing "oc get pods -n openshift-monitoring" succeeds
+        Then stdout matches ".*cluster-monitoring-operator-\w+-\w+\ *2/2\ *Running.*"
+        And unsetting config property "enable-cluster-monitoring" succeeds
+        And unsetting config property "memory" succeeds
+
     @darwin @linux @windows
     Scenario: CRC forcible stop
         When executing "crc stop -f"
@@ -181,7 +191,7 @@ Feature: Basic test
     Scenario Outline: CRC clean-up
         When executing "crc cleanup" succeeds
         Then stderr should contain "Uninstalling tray if installed"
-        And stderr should contain "Will run as admin: Uninstalling CodeReady Containers System Tray"
+        Then stderr should contain "Uninstalling daemon if installed"
         And stderr should contain "Removing the crc VM if exists"
         And stderr should contain "Removing dns server from interface"
         And stderr should contain "Will run as admin: Remove dns entry for default switch"