diff --git a/libcontainer/capabilities_linux.go b/libcontainer/capabilities_linux.go index 7c66f572..9daef29e 100644 --- a/libcontainer/capabilities_linux.go +++ b/libcontainer/capabilities_linux.go @@ -71,7 +71,11 @@ func newContainerCapList(capConfig *configs.Capabilities) (*containerCapabilitie } ambient = append(ambient, v) } - pid, err := capability.NewPid(0) + pid, err := capability.NewPid2(0) + if err != nil { + return nil, err + } + err = pid.Load() if err != nil { return nil, err } diff --git a/tests/integration/spec.bats b/tests/integration/spec.bats index 059e4c05..5df8f703 100644 --- a/tests/integration/spec.bats +++ b/tests/integration/spec.bats @@ -72,7 +72,7 @@ function teardown() { run git clone https://github.com/opencontainers/runtime-spec.git src/runtime-spec [ "$status" -eq 0 ] - SPEC_COMMIT=$(grep '^github.com/opencontainers/runtime-spec' ${TESTDIR}/../../vendor.conf | cut -d ' ' -f 2) + SPEC_COMMIT=$(grep '^github.com/opencontainers/runtime-spec' ${TESTDIR}/../../vendor.conf | tr -s ' ' | cut -d ' ' -f 2) run git -C src/runtime-spec reset --hard "${SPEC_COMMIT}" [ "$status" -eq 0 ] diff --git a/vendor.conf b/vendor.conf index 09fd7056..e3f8e6d7 100644 --- a/vendor.conf +++ b/vendor.conf @@ -1,25 +1,28 @@ # OCI runtime-spec. When updating this, make sure you use a version tag rather # than a commit ID so it's much more obvious what version of the spec we are # using. -github.com/opencontainers/runtime-spec 29686dbc5559d93fb1ef402eeda3e35c38d75af4 +github.com/opencontainers/runtime-spec 29686dbc5559d93fb1ef402eeda3e35c38d75af4 # v1.0.1-59-g29686db + # Core libcontainer functionality. -github.com/checkpoint-restore/go-criu v3.11 -github.com/mrunalp/fileutils ed869b029674c0e9ce4c0dfa781405c2d9946d08 -github.com/opencontainers/selinux v1.2.2 -github.com/seccomp/libseccomp-golang v0.9.1 -github.com/sirupsen/logrus 8bdbc7bcc01dcbb8ec23dc8a28e332258d25251f -github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 -github.com/vishvananda/netlink 1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270 +github.com/checkpoint-restore/go-criu 17b0214f6c48980c45dc47ecb0cfd6d9e02df723 # v3.11 +github.com/mrunalp/fileutils 7d4729fb36185a7c1719923406c9d40e54fb93c7 +github.com/opencontainers/selinux 3a1f366feb7aecbf7a0e71ac4cea88b31597de9e # v1.2.2 +github.com/seccomp/libseccomp-golang 689e3c1541a84461afc49c1c87352a6cedf72e9c # v0.9.1 +github.com/sirupsen/logrus 8bdbc7bcc01dcbb8ec23dc8a28e332258d25251f # v1.4.1 +github.com/syndtr/gocapability d98352740cb2c55f81556b63d4a1ec64c5a319c2 +github.com/vishvananda/netlink 1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270 + # systemd integration. -github.com/coreos/go-systemd v14 -github.com/godbus/dbus v3 -github.com/golang/protobuf 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8 +github.com/coreos/go-systemd 95778dfbb74eb7e4dbaf43bf7d71809650ef8076 # v19 +github.com/godbus/dbus 2ff6f7ffd60f0f2410b3105864bdd12c7894f844 # v5.0.1 +github.com/golang/protobuf 925541529c1fa6821df4e44ce2723319eb2be768 # v1.0.0 + # Command-line interface. -github.com/cyphar/filepath-securejoin v0.2.1 -github.com/docker/go-units v0.2.0 -github.com/urfave/cli d53eb991652b1d438abdd34ce4bfa3ef1539108e -golang.org/x/sys 9eafafc0a87e0fd0aeeba439a4573537970c44c7 https://github.com/golang/sys +github.com/cyphar/filepath-securejoin a261ee33d7a517f054effbf451841abaafe3e0fd # v0.2.2 +github.com/docker/go-units 47565b4f722fb6ceae66b95f853feed578a4a51c # v0.3.3 +github.com/urfave/cli cfb38830724cc34fedffe9a2a29fb54fa9169cd1 # v1.20.0 +golang.org/x/sys 9eafafc0a87e0fd0aeeba439a4573537970c44c7 https://github.com/golang/sys # console dependencies -github.com/containerd/console 2748ece16665b45a47f884001d5831ec79703880 -github.com/pkg/errors v0.8.0 +github.com/containerd/console 0650fd9eeb50bab4fc99dceb9f2e14cf58f36e7f +github.com/pkg/errors ba968bfe8b2f7e042a574c888954fccecfa385b4 # v0.8.1 diff --git a/vendor/github.com/containerd/console/LICENSE b/vendor/github.com/containerd/console/LICENSE index 261eeb9e..584149b6 100644 --- a/vendor/github.com/containerd/console/LICENSE +++ b/vendor/github.com/containerd/console/LICENSE @@ -1,6 +1,7 @@ + Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -175,24 +176,13 @@ END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] + Copyright The containerd Authors 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 + https://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, diff --git a/vendor/github.com/containerd/console/README.md b/vendor/github.com/containerd/console/README.md index 4c56d9d1..5392fdaf 100644 --- a/vendor/github.com/containerd/console/README.md +++ b/vendor/github.com/containerd/console/README.md @@ -15,3 +15,13 @@ if err := current.SetRaw(); err != nil { ws, err := current.Size() current.Resize(ws) ``` + +## Project details + +console is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/vendor/github.com/containerd/console/console.go b/vendor/github.com/containerd/console/console.go index bf2798fd..c187a9b4 100644 --- a/vendor/github.com/containerd/console/console.go +++ b/vendor/github.com/containerd/console/console.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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 console import ( diff --git a/vendor/github.com/containerd/console/console_linux.go b/vendor/github.com/containerd/console/console_linux.go index c9637292..42274e10 100644 --- a/vendor/github.com/containerd/console/console_linux.go +++ b/vendor/github.com/containerd/console/console_linux.go @@ -1,5 +1,21 @@ // +build linux +/* + Copyright The containerd Authors. + + 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 console import ( @@ -56,7 +72,7 @@ func NewEpoller() (*Epoller, error) { }, nil } -// Add creates a epoll console based on the provided console. The console will +// Add creates an epoll console based on the provided console. The console will // be registered with EPOLLET (i.e. using edge-triggered notification) and its // file descriptor will be set to non-blocking mode. After this, user should use // the return console to perform I/O. @@ -118,7 +134,7 @@ func (e *Epoller) Wait() error { } } -// Close unregister the console's file descriptor from epoll interface +// CloseConsole unregisters the console's file descriptor from epoll interface func (e *Epoller) CloseConsole(fd int) error { e.mu.Lock() defer e.mu.Unlock() @@ -133,12 +149,12 @@ func (e *Epoller) getConsole(sysfd int) *EpollConsole { return f } -// Close the epoll fd +// Close closes the epoll fd func (e *Epoller) Close() error { return unix.Close(e.efd) } -// EpollConsole acts like a console but register its file descriptor with a +// EpollConsole acts like a console but registers its file descriptor with an // epoll fd and uses epoll API to perform I/O. type EpollConsole struct { Console @@ -151,7 +167,7 @@ type EpollConsole struct { // Read reads up to len(p) bytes into p. It returns the number of bytes read // (0 <= n <= len(p)) and any error encountered. // -// If the console's read returns EAGAIN or EIO, we assumes that its a +// If the console's read returns EAGAIN or EIO, we assume that it's a // temporary error because the other side went away and wait for the signal // generated by epoll event to continue. func (ec *EpollConsole) Read(p []byte) (n int, err error) { @@ -191,7 +207,7 @@ func (ec *EpollConsole) Read(p []byte) (n int, err error) { // written from p (0 <= n <= len(p)) and any error encountered that caused // the write to stop early. // -// If writes to the console returns EAGAIN or EIO, we assumes that its a +// If writes to the console returns EAGAIN or EIO, we assume that it's a // temporary error because the other side went away and wait for the signal // generated by epoll event to continue. func (ec *EpollConsole) Write(p []byte) (n int, err error) { @@ -208,7 +224,7 @@ func (ec *EpollConsole) Write(p []byte) (n int, err error) { } else { hangup = (err == unix.EAGAIN || err == unix.EIO) } - // if the other end disappear, assume this is temporary and wait for the + // if the other end disappears, assume this is temporary and wait for the // signal to continue again. if hangup { ec.writec.Wait() @@ -226,7 +242,7 @@ func (ec *EpollConsole) Write(p []byte) (n int, err error) { return n, err } -// Close closed the file descriptor and signal call waiters for this fd. +// Shutdown closes the file descriptor and signals call waiters for this fd. // It accepts a callback which will be called with the console's fd. The // callback typically will be used to do further cleanup such as unregister the // console's fd from the epoll interface. @@ -246,10 +262,14 @@ func (ec *EpollConsole) Shutdown(close func(int) error) error { // signalRead signals that the console is readable. func (ec *EpollConsole) signalRead() { + ec.readc.L.Lock() ec.readc.Signal() + ec.readc.L.Unlock() } // signalWrite signals that the console is writable. func (ec *EpollConsole) signalWrite() { + ec.writec.L.Lock() ec.writec.Signal() + ec.writec.L.Unlock() } diff --git a/vendor/github.com/containerd/console/console_unix.go b/vendor/github.com/containerd/console/console_unix.go index 118c8c3a..a4a8d126 100644 --- a/vendor/github.com/containerd/console/console_unix.go +++ b/vendor/github.com/containerd/console/console_unix.go @@ -1,4 +1,20 @@ -// +build darwin freebsd linux solaris +// +build darwin freebsd linux openbsd solaris + +/* + Copyright The containerd Authors. + + 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 console diff --git a/vendor/github.com/containerd/console/console_windows.go b/vendor/github.com/containerd/console/console_windows.go index d78a0b84..62dbe1c0 100644 --- a/vendor/github.com/containerd/console/console_windows.go +++ b/vendor/github.com/containerd/console/console_windows.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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 console import ( @@ -134,11 +150,11 @@ func (m *master) Close() error { } func (m *master) Read(b []byte) (int, error) { - panic("not implemented on windows") + return os.Stdin.Read(b) } func (m *master) Write(b []byte) (int, error) { - panic("not implemented on windows") + return os.Stdout.Write(b) } func (m *master) Fd() uintptr { diff --git a/vendor/github.com/containerd/console/tc_darwin.go b/vendor/github.com/containerd/console/tc_darwin.go index b102bad7..b0128abb 100644 --- a/vendor/github.com/containerd/console/tc_darwin.go +++ b/vendor/github.com/containerd/console/tc_darwin.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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 console import ( diff --git a/vendor/github.com/containerd/console/tc_freebsd.go b/vendor/github.com/containerd/console/tc_freebsd.go index e2a10e44..04583a61 100644 --- a/vendor/github.com/containerd/console/tc_freebsd.go +++ b/vendor/github.com/containerd/console/tc_freebsd.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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 console import ( diff --git a/vendor/github.com/containerd/console/tc_linux.go b/vendor/github.com/containerd/console/tc_linux.go index 73330eb7..1bdd68e6 100644 --- a/vendor/github.com/containerd/console/tc_linux.go +++ b/vendor/github.com/containerd/console/tc_linux.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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 console import ( diff --git a/vendor/github.com/containerd/console/tc_openbsd_cgo.go b/vendor/github.com/containerd/console/tc_openbsd_cgo.go new file mode 100644 index 00000000..f0cec06a --- /dev/null +++ b/vendor/github.com/containerd/console/tc_openbsd_cgo.go @@ -0,0 +1,51 @@ +// +build openbsd,cgo + +/* + Copyright The containerd Authors. + + 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 console + +import ( + "os" + + "golang.org/x/sys/unix" +) + +//#include +import "C" + +const ( + cmdTcGet = unix.TIOCGETA + cmdTcSet = unix.TIOCSETA +) + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + ptspath, err := C.ptsname(C.int(f.Fd())) + if err != nil { + return "", err + } + return C.GoString(ptspath), nil +} + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + if _, err := C.grantpt(C.int(f.Fd())); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/containerd/console/tc_openbsd_nocgo.go b/vendor/github.com/containerd/console/tc_openbsd_nocgo.go new file mode 100644 index 00000000..daccce20 --- /dev/null +++ b/vendor/github.com/containerd/console/tc_openbsd_nocgo.go @@ -0,0 +1,47 @@ +// +build openbsd,!cgo + +/* + Copyright The containerd Authors. + + 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. +*/ + +// +// Implementing the functions below requires cgo support. Non-cgo stubs +// versions are defined below to enable cross-compilation of source code +// that depends on these functions, but the resultant cross-compiled +// binaries cannot actually be used. If the stub function(s) below are +// actually invoked they will display an error message and cause the +// calling process to exit. +// + +package console + +import ( + "os" + + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TIOCGETA + cmdTcSet = unix.TIOCSETA +) + +func ptsname(f *os.File) (string, error) { + panic("ptsname() support requires cgo.") +} + +func unlockpt(f *os.File) error { + panic("unlockpt() support requires cgo.") +} diff --git a/vendor/github.com/containerd/console/tc_solaris_cgo.go b/vendor/github.com/containerd/console/tc_solaris_cgo.go index f8066d8e..e36a68ed 100644 --- a/vendor/github.com/containerd/console/tc_solaris_cgo.go +++ b/vendor/github.com/containerd/console/tc_solaris_cgo.go @@ -1,5 +1,21 @@ // +build solaris,cgo +/* + Copyright The containerd Authors. + + 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 console import ( diff --git a/vendor/github.com/containerd/console/tc_solaris_nocgo.go b/vendor/github.com/containerd/console/tc_solaris_nocgo.go index 0aefa0d2..eb0bd2c3 100644 --- a/vendor/github.com/containerd/console/tc_solaris_nocgo.go +++ b/vendor/github.com/containerd/console/tc_solaris_nocgo.go @@ -1,5 +1,21 @@ // +build solaris,!cgo +/* + Copyright The containerd Authors. + + 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. +*/ + // // Implementing the functions below requires cgo support. Non-cgo stubs // versions are defined below to enable cross-compilation of source code diff --git a/vendor/github.com/containerd/console/tc_unix.go b/vendor/github.com/containerd/console/tc_unix.go index df7dcb93..7ae773c5 100644 --- a/vendor/github.com/containerd/console/tc_unix.go +++ b/vendor/github.com/containerd/console/tc_unix.go @@ -1,4 +1,20 @@ -// +build darwin freebsd linux solaris +// +build darwin freebsd linux openbsd solaris + +/* + Copyright The containerd Authors. + + 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 console diff --git a/vendor/github.com/coreos/go-systemd/NOTICE b/vendor/github.com/coreos/go-systemd/NOTICE new file mode 100644 index 00000000..23a0ada2 --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/NOTICE @@ -0,0 +1,5 @@ +CoreOS Project +Copyright 2018 CoreOS, Inc + +This product includes software developed at CoreOS, Inc. +(http://www.coreos.com/). diff --git a/vendor/github.com/coreos/go-systemd/README.md b/vendor/github.com/coreos/go-systemd/README.md index cb87a112..cde3a8f9 100644 --- a/vendor/github.com/coreos/go-systemd/README.md +++ b/vendor/github.com/coreos/go-systemd/README.md @@ -2,13 +2,17 @@ [![Build Status](https://travis-ci.org/coreos/go-systemd.png?branch=master)](https://travis-ci.org/coreos/go-systemd) [![godoc](https://godoc.org/github.com/coreos/go-systemd?status.svg)](http://godoc.org/github.com/coreos/go-systemd) +![minimum golang 1.10](https://img.shields.io/badge/golang-1.10%2B-orange.svg) + Go bindings to systemd. The project has several packages: - `activation` - for writing and using socket activation from Go +- `daemon` - for notifying systemd of service status changes - `dbus` - for starting/stopping/inspecting running services and units - `journal` - for writing to systemd's logging service, journald - `sdjournal` - for reading from journald by wrapping its C API +- `login1` - for integration with the systemd logind API - `machine1` - for registering machines/containers with systemd - `unit` - for (de)serialization and comparison of unit files @@ -18,10 +22,9 @@ An example HTTP server using socket activation can be quickly set up by followin https://github.com/coreos/go-systemd/tree/master/examples/activation/httpserver -## Journal +## systemd Service Notification -Using the pure-Go `journal` package you can submit journal entries directly to systemd's journal, taking advantage of features like indexed key/value pairs for each log entry. -The `sdjournal` package provides read access to the journal by wrapping around journald's native C API; consequently it requires cgo and the journal headers to be available. +The `daemon` package is an implementation of the [sd_notify protocol](https://www.freedesktop.org/software/systemd/man/sd_notify.html#Description). It can be used to inform systemd of service start-up completion, watchdog events, and other status changes. ## D-Bus @@ -45,6 +48,20 @@ Create `/etc/dbus-1/system-local.conf` that looks like this: ``` +## Journal + +### Writing to the Journal + +Using the pure-Go `journal` package you can submit journal entries directly to systemd's journal, taking advantage of features like indexed key/value pairs for each log entry. + +### Reading from the Journal + +The `sdjournal` package provides read access to the journal by wrapping around journald's native C API; consequently it requires cgo and the journal headers to be available. + +## logind + +The `login1` package provides functions to integrate with the [systemd logind API](http://www.freedesktop.org/wiki/Software/systemd/logind/). + ## machined The `machine1` package allows interaction with the [systemd machined D-Bus API](http://www.freedesktop.org/wiki/Software/systemd/machined/). diff --git a/vendor/github.com/coreos/go-systemd/activation/files.go b/vendor/github.com/coreos/go-systemd/activation/files.go index c8e85fcd..29dd18de 100644 --- a/vendor/github.com/coreos/go-systemd/activation/files.go +++ b/vendor/github.com/coreos/go-systemd/activation/files.go @@ -18,18 +18,26 @@ package activation import ( "os" "strconv" + "strings" "syscall" ) -// based on: https://gist.github.com/alberts/4640792 const ( + // listenFdsStart corresponds to `SD_LISTEN_FDS_START`. listenFdsStart = 3 ) +// Files returns a slice containing a `os.File` object for each +// file descriptor passed to this process via systemd fd-passing protocol. +// +// The order of the file descriptors is preserved in the returned slice. +// `unsetEnv` is typically set to `true` in order to avoid clashes in +// fd usage and to avoid leaking environment flags to child processes. func Files(unsetEnv bool) []*os.File { if unsetEnv { defer os.Unsetenv("LISTEN_PID") defer os.Unsetenv("LISTEN_FDS") + defer os.Unsetenv("LISTEN_FDNAMES") } pid, err := strconv.Atoi(os.Getenv("LISTEN_PID")) @@ -42,10 +50,17 @@ func Files(unsetEnv bool) []*os.File { return nil } + names := strings.Split(os.Getenv("LISTEN_FDNAMES"), ":") + files := make([]*os.File, 0, nfds) for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ { syscall.CloseOnExec(fd) - files = append(files, os.NewFile(uintptr(fd), "LISTEN_FD_"+strconv.Itoa(fd))) + name := "LISTEN_FD_" + strconv.Itoa(fd) + offset := fd - listenFdsStart + if offset < len(names) && len(names[offset]) > 0 { + name = names[offset] + } + files = append(files, os.NewFile(uintptr(fd), name)) } return files diff --git a/vendor/github.com/coreos/go-systemd/activation/listeners.go b/vendor/github.com/coreos/go-systemd/activation/listeners.go index fd5dfc70..bb5cc231 100644 --- a/vendor/github.com/coreos/go-systemd/activation/listeners.go +++ b/vendor/github.com/coreos/go-systemd/activation/listeners.go @@ -25,13 +25,33 @@ import ( // The order of the file descriptors is preserved in the returned slice. // Nil values are used to fill any gaps. For example if systemd were to return file descriptors // corresponding with "udp, tcp, tcp", then the slice would contain {nil, net.Listener, net.Listener} -func Listeners(unsetEnv bool) ([]net.Listener, error) { - files := Files(unsetEnv) +func Listeners() ([]net.Listener, error) { + files := Files(true) listeners := make([]net.Listener, len(files)) for i, f := range files { if pc, err := net.FileListener(f); err == nil { listeners[i] = pc + f.Close() + } + } + return listeners, nil +} + +// ListenersWithNames maps a listener name to a set of net.Listener instances. +func ListenersWithNames() (map[string][]net.Listener, error) { + files := Files(true) + listeners := map[string][]net.Listener{} + + for _, f := range files { + if pc, err := net.FileListener(f); err == nil { + current, ok := listeners[f.Name()] + if !ok { + listeners[f.Name()] = []net.Listener{pc} + } else { + listeners[f.Name()] = append(current, pc) + } + f.Close() } } return listeners, nil @@ -40,8 +60,8 @@ func Listeners(unsetEnv bool) ([]net.Listener, error) { // TLSListeners returns a slice containing a net.listener for each matching TCP socket type // passed to this process. // It uses default Listeners func and forces TCP sockets handlers to use TLS based on tlsConfig. -func TLSListeners(unsetEnv bool, tlsConfig *tls.Config) ([]net.Listener, error) { - listeners, err := Listeners(unsetEnv) +func TLSListeners(tlsConfig *tls.Config) ([]net.Listener, error) { + listeners, err := Listeners() if listeners == nil || err != nil { return nil, err @@ -58,3 +78,26 @@ func TLSListeners(unsetEnv bool, tlsConfig *tls.Config) ([]net.Listener, error) return listeners, err } + +// TLSListenersWithNames maps a listener name to a net.Listener with +// the associated TLS configuration. +func TLSListenersWithNames(tlsConfig *tls.Config) (map[string][]net.Listener, error) { + listeners, err := ListenersWithNames() + + if listeners == nil || err != nil { + return nil, err + } + + if tlsConfig != nil && err == nil { + for _, ll := range listeners { + // Activate TLS only for TCP sockets + for i, l := range ll { + if l.Addr().Network() == "tcp" { + ll[i] = tls.NewListener(l, tlsConfig) + } + } + } + } + + return listeners, err +} diff --git a/vendor/github.com/coreos/go-systemd/activation/packetconns.go b/vendor/github.com/coreos/go-systemd/activation/packetconns.go index 48b2ca02..a9720678 100644 --- a/vendor/github.com/coreos/go-systemd/activation/packetconns.go +++ b/vendor/github.com/coreos/go-systemd/activation/packetconns.go @@ -24,13 +24,14 @@ import ( // The order of the file descriptors is preserved in the returned slice. // Nil values are used to fill any gaps. For example if systemd were to return file descriptors // corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn} -func PacketConns(unsetEnv bool) ([]net.PacketConn, error) { - files := Files(unsetEnv) +func PacketConns() ([]net.PacketConn, error) { + files := Files(true) conns := make([]net.PacketConn, len(files)) for i, f := range files { if pc, err := net.FilePacketConn(f); err == nil { conns[i] = pc + f.Close() } } return conns, nil diff --git a/vendor/github.com/coreos/go-systemd/dbus/dbus.go b/vendor/github.com/coreos/go-systemd/dbus/dbus.go index c1694fb5..f652582e 100644 --- a/vendor/github.com/coreos/go-systemd/dbus/dbus.go +++ b/vendor/github.com/coreos/go-systemd/dbus/dbus.go @@ -16,6 +16,7 @@ package dbus import ( + "encoding/hex" "fmt" "os" "strconv" @@ -60,6 +61,27 @@ func PathBusEscape(path string) string { return string(n) } +// pathBusUnescape is the inverse of PathBusEscape. +func pathBusUnescape(path string) string { + if path == "_" { + return "" + } + n := []byte{} + for i := 0; i < len(path); i++ { + c := path[i] + if c == '_' && i+2 < len(path) { + res, err := hex.DecodeString(path[i+1 : i+3]) + if err == nil { + n = append(n, res...) + } + i += 2 + } else { + n = append(n, c) + } + } + return string(n) +} + // Conn is a connection to systemd's dbus endpoint. type Conn struct { // sysconn/sysobj are only used to call dbus methods @@ -74,13 +96,18 @@ type Conn struct { jobs map[dbus.ObjectPath]chan<- string sync.Mutex } - subscriber struct { + subStateSubscriber struct { updateCh chan<- *SubStateUpdate errCh chan<- error sync.Mutex ignore map[dbus.ObjectPath]int64 cleanIgnore int64 } + propertiesSubscriber struct { + updateCh chan<- *PropertiesUpdate + errCh chan<- error + sync.Mutex + } } // New establishes a connection to any available bus and authenticates. @@ -116,7 +143,7 @@ func NewUserConnection() (*Conn, error) { func NewSystemdConnection() (*Conn, error) { return NewConnection(func() (*dbus.Conn, error) { // We skip Hello when talking directly to systemd. - return dbusAuthConnection(func() (*dbus.Conn, error) { + return dbusAuthConnection(func(opts ...dbus.ConnOption) (*dbus.Conn, error) { return dbus.Dial("unix:path=/run/systemd/private") }) }) @@ -152,7 +179,7 @@ func NewConnection(dialBus func() (*dbus.Conn, error)) (*Conn, error) { sigobj: systemdObject(sigconn), } - c.subscriber.ignore = make(map[dbus.ObjectPath]int64) + c.subStateSubscriber.ignore = make(map[dbus.ObjectPath]int64) c.jobListener.jobs = make(map[dbus.ObjectPath]chan<- string) // Setup the listeners on jobs so that we can get completions @@ -174,7 +201,7 @@ func (c *Conn) GetManagerProperty(prop string) (string, error) { return variant.String(), nil } -func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) { +func dbusAuthConnection(createBus func(opts ...dbus.ConnOption) (*dbus.Conn, error)) (*dbus.Conn, error) { conn, err := createBus() if err != nil { return nil, err @@ -194,7 +221,7 @@ func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error return conn, nil } -func dbusAuthHelloConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) { +func dbusAuthHelloConnection(createBus func(opts ...dbus.ConnOption) (*dbus.Conn, error)) (*dbus.Conn, error) { conn, err := dbusAuthConnection(createBus) if err != nil { return nil, err diff --git a/vendor/github.com/coreos/go-systemd/dbus/methods.go b/vendor/github.com/coreos/go-systemd/dbus/methods.go index ab17f7cc..5f2790ac 100644 --- a/vendor/github.com/coreos/go-systemd/dbus/methods.go +++ b/vendor/github.com/coreos/go-systemd/dbus/methods.go @@ -1,4 +1,4 @@ -// Copyright 2015 CoreOS, Inc. +// Copyright 2015, 2018 CoreOS, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package dbus import ( "errors" + "fmt" "path" "strconv" @@ -116,13 +117,13 @@ func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, return c.startJob(ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode) } -// ReloadOrRestart attempts a reload if the unit supports it and use a restart +// ReloadOrRestartUnit attempts a reload if the unit supports it and use a restart // otherwise. func (c *Conn) ReloadOrRestartUnit(name string, mode string, ch chan<- string) (int, error) { return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode) } -// ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try" +// ReloadOrTryRestartUnit attempts a reload if the unit supports it and use a "Try" // flavored restart otherwise. func (c *Conn) ReloadOrTryRestartUnit(name string, mode string, ch chan<- string) (int, error) { return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode) @@ -148,14 +149,27 @@ func (c *Conn) ResetFailedUnit(name string) error { return c.sysobj.Call("org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store() } -// getProperties takes the unit name and returns all of its dbus object properties, for the given dbus interface -func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]interface{}, error) { +// SystemState returns the systemd state. Equivalent to `systemctl is-system-running`. +func (c *Conn) SystemState() (*Property, error) { + var err error + var prop dbus.Variant + + obj := c.sysconn.Object("org.freedesktop.systemd1", "/org/freedesktop/systemd1") + err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.systemd1.Manager", "SystemState").Store(&prop) + if err != nil { + return nil, err + } + + return &Property{Name: "SystemState", Value: prop}, nil +} + +// getProperties takes the unit path and returns all of its dbus object properties, for the given dbus interface +func (c *Conn) getProperties(path dbus.ObjectPath, dbusInterface string) (map[string]interface{}, error) { var err error var props map[string]dbus.Variant - path := unitPath(unit) if !path.IsValid() { - return nil, errors.New("invalid unit name: " + unit) + return nil, fmt.Errorf("invalid unit name: %v", path) } obj := c.sysconn.Object("org.freedesktop.systemd1", path) @@ -172,9 +186,15 @@ func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]inte return out, nil } -// GetUnitProperties takes the unit name and returns all of its dbus object properties. +// GetUnitProperties takes the (unescaped) unit name and returns all of its dbus object properties. func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) { - return c.getProperties(unit, "org.freedesktop.systemd1.Unit") + path := unitPath(unit) + return c.getProperties(path, "org.freedesktop.systemd1.Unit") +} + +// GetUnitPathProperties takes the (escaped) unit path and returns all of its dbus object properties. +func (c *Conn) GetUnitPathProperties(path dbus.ObjectPath) (map[string]interface{}, error) { + return c.getProperties(path, "org.freedesktop.systemd1.Unit") } func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) { @@ -208,7 +228,8 @@ func (c *Conn) GetServiceProperty(service string, propertyName string) (*Propert // Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope // return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) { - return c.getProperties(unit, "org.freedesktop.systemd1."+unitType) + path := unitPath(unit) + return c.getProperties(path, "org.freedesktop.systemd1."+unitType) } // SetUnitProperties() may be used to modify certain unit properties at runtime. @@ -270,6 +291,8 @@ func (c *Conn) listUnitsInternal(f storeFunc) ([]UnitStatus, error) { // ListUnits returns an array with all currently loaded units. Note that // units may be known by multiple names at the same time, and hence there might // be more unit names loaded than actual units behind them. +// Also note that a unit is only loaded if it is active and/or enabled. +// Units that are both disabled and inactive will thus not be returned. func (c *Conn) ListUnits() ([]UnitStatus, error) { return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store) } @@ -292,6 +315,7 @@ func (c *Conn) ListUnitsByPatterns(states []string, patterns []string) ([]UnitSt // names and returns an UnitStatus array. Comparing to ListUnitsByPatterns // method, this method returns statuses even for inactive or non-existing // units. Input array should contain exact unit names, but not patterns. +// Note: Requires systemd v230 or higher func (c *Conn) ListUnitsByNames(units []string) ([]UnitStatus, error) { return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsByNames", 0, units).Store) } @@ -563,3 +587,8 @@ func (c *Conn) Reload() error { func unitPath(name string) dbus.ObjectPath { return dbus.ObjectPath("/org/freedesktop/systemd1/unit/" + PathBusEscape(name)) } + +// unitName returns the unescaped base element of the supplied escaped path +func unitName(dpath dbus.ObjectPath) string { + return pathBusUnescape(path.Base(string(dpath))) +} diff --git a/vendor/github.com/coreos/go-systemd/dbus/set.go b/vendor/github.com/coreos/go-systemd/dbus/set.go index f92e6fbe..17c5d485 100644 --- a/vendor/github.com/coreos/go-systemd/dbus/set.go +++ b/vendor/github.com/coreos/go-systemd/dbus/set.go @@ -36,7 +36,7 @@ func (s *set) Length() int { } func (s *set) Values() (values []string) { - for val, _ := range s.data { + for val := range s.data { values = append(values, val) } return diff --git a/vendor/github.com/coreos/go-systemd/dbus/subscription.go b/vendor/github.com/coreos/go-systemd/dbus/subscription.go index 99645144..f6d7a08a 100644 --- a/vendor/github.com/coreos/go-systemd/dbus/subscription.go +++ b/vendor/github.com/coreos/go-systemd/dbus/subscription.go @@ -16,6 +16,7 @@ package dbus import ( "errors" + "log" "time" "github.com/godbus/dbus" @@ -36,22 +37,12 @@ func (c *Conn) Subscribe() error { c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'") - err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store() - if err != nil { - return err - } - - return nil + return c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store() } // Unsubscribe this connection from systemd dbus events. func (c *Conn) Unsubscribe() error { - err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store() - if err != nil { - return err - } - - return nil + return c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store() } func (c *Conn) dispatch() { @@ -70,7 +61,8 @@ func (c *Conn) dispatch() { c.jobComplete(signal) } - if c.subscriber.updateCh == nil { + if c.subStateSubscriber.updateCh == nil && + c.propertiesSubscriber.updateCh == nil { continue } @@ -84,6 +76,12 @@ func (c *Conn) dispatch() { case "org.freedesktop.DBus.Properties.PropertiesChanged": if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" { unitPath = signal.Path + + if len(signal.Body) >= 2 { + if changed, ok := signal.Body[1].(map[string]dbus.Variant); ok { + c.sendPropertiesUpdate(unitPath, changed) + } + } } } @@ -96,7 +94,7 @@ func (c *Conn) dispatch() { }() } -// Returns two unbuffered channels which will receive all changed units every +// SubscribeUnits returns two unbuffered channels which will receive all changed units every // interval. Deleted units are sent as nil. func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) { return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil) @@ -169,42 +167,80 @@ type SubStateUpdate struct { // is full, it attempts to write an error to errCh; if errCh is full, the error // passes silently. func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) { - c.subscriber.Lock() - defer c.subscriber.Unlock() - c.subscriber.updateCh = updateCh - c.subscriber.errCh = errCh -} - -func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) { - c.subscriber.Lock() - defer c.subscriber.Unlock() - - if c.shouldIgnore(path) { + if c == nil { + msg := "nil receiver" + select { + case errCh <- errors.New(msg): + default: + log.Printf("full error channel while reporting: %s\n", msg) + } return } - info, err := c.GetUnitProperties(string(path)) - if err != nil { - select { - case c.subscriber.errCh <- err: - default: - } + c.subStateSubscriber.Lock() + defer c.subStateSubscriber.Unlock() + c.subStateSubscriber.updateCh = updateCh + c.subStateSubscriber.errCh = errCh +} + +func (c *Conn) sendSubStateUpdate(unitPath dbus.ObjectPath) { + c.subStateSubscriber.Lock() + defer c.subStateSubscriber.Unlock() + + if c.subStateSubscriber.updateCh == nil { + return } - name := info["Id"].(string) - substate := info["SubState"].(string) + isIgnored := c.shouldIgnore(unitPath) + defer c.cleanIgnore() + if isIgnored { + return + } + + info, err := c.GetUnitPathProperties(unitPath) + if err != nil { + select { + case c.subStateSubscriber.errCh <- err: + default: + log.Printf("full error channel while reporting: %s\n", err) + } + return + } + defer c.updateIgnore(unitPath, info) + + name, ok := info["Id"].(string) + if !ok { + msg := "failed to cast info.Id" + select { + case c.subStateSubscriber.errCh <- errors.New(msg): + default: + log.Printf("full error channel while reporting: %s\n", err) + } + return + } + substate, ok := info["SubState"].(string) + if !ok { + msg := "failed to cast info.SubState" + select { + case c.subStateSubscriber.errCh <- errors.New(msg): + default: + log.Printf("full error channel while reporting: %s\n", msg) + } + return + } update := &SubStateUpdate{name, substate} select { - case c.subscriber.updateCh <- update: + case c.subStateSubscriber.updateCh <- update: default: + msg := "update channel is full" select { - case c.subscriber.errCh <- errors.New("update channel full!"): + case c.subStateSubscriber.errCh <- errors.New(msg): default: + log.Printf("full error channel while reporting: %s\n", msg) } + return } - - c.updateIgnore(path, info) } // The ignore functions work around a wart in the systemd dbus interface. @@ -222,29 +258,76 @@ func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) { // the properties). func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool { - t, ok := c.subscriber.ignore[path] + t, ok := c.subStateSubscriber.ignore[path] return ok && t >= time.Now().UnixNano() } func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) { - c.cleanIgnore() + loadState, ok := info["LoadState"].(string) + if !ok { + return + } // unit is unloaded - it will trigger bad systemd dbus behavior - if info["LoadState"].(string) == "not-found" { - c.subscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval + if loadState == "not-found" { + c.subStateSubscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval } } // without this, ignore would grow unboundedly over time func (c *Conn) cleanIgnore() { now := time.Now().UnixNano() - if c.subscriber.cleanIgnore < now { - c.subscriber.cleanIgnore = now + cleanIgnoreInterval + if c.subStateSubscriber.cleanIgnore < now { + c.subStateSubscriber.cleanIgnore = now + cleanIgnoreInterval - for p, t := range c.subscriber.ignore { + for p, t := range c.subStateSubscriber.ignore { if t < now { - delete(c.subscriber.ignore, p) + delete(c.subStateSubscriber.ignore, p) } } } } + +// PropertiesUpdate holds a map of a unit's changed properties +type PropertiesUpdate struct { + UnitName string + Changed map[string]dbus.Variant +} + +// SetPropertiesSubscriber writes to updateCh when any unit's properties +// change. Every property change reported by systemd will be sent; that is, no +// transitions will be "missed" (as they might be with SetSubStateSubscriber). +// However, state changes will only be written to the channel with non-blocking +// writes. If updateCh is full, it attempts to write an error to errCh; if +// errCh is full, the error passes silently. +func (c *Conn) SetPropertiesSubscriber(updateCh chan<- *PropertiesUpdate, errCh chan<- error) { + c.propertiesSubscriber.Lock() + defer c.propertiesSubscriber.Unlock() + c.propertiesSubscriber.updateCh = updateCh + c.propertiesSubscriber.errCh = errCh +} + +// we don't need to worry about shouldIgnore() here because +// sendPropertiesUpdate doesn't call GetProperties() +func (c *Conn) sendPropertiesUpdate(unitPath dbus.ObjectPath, changedProps map[string]dbus.Variant) { + c.propertiesSubscriber.Lock() + defer c.propertiesSubscriber.Unlock() + + if c.propertiesSubscriber.updateCh == nil { + return + } + + update := &PropertiesUpdate{unitName(unitPath), changedProps} + + select { + case c.propertiesSubscriber.updateCh <- update: + default: + msg := "update channel is full" + select { + case c.propertiesSubscriber.errCh <- errors.New(msg): + default: + log.Printf("full error channel while reporting: %s\n", msg) + } + return + } +} diff --git a/vendor/github.com/cyphar/filepath-securejoin/join.go b/vendor/github.com/cyphar/filepath-securejoin/join.go index f2098547..c4ca3d71 100644 --- a/vendor/github.com/cyphar/filepath-securejoin/join.go +++ b/vendor/github.com/cyphar/filepath-securejoin/join.go @@ -12,7 +12,6 @@ package securejoin import ( "bytes" - "fmt" "os" "path/filepath" "strings" @@ -23,7 +22,7 @@ import ( // ErrSymlinkLoop is returned by SecureJoinVFS when too many symlinks have been // evaluated in attempting to securely join the two given paths. -var ErrSymlinkLoop = fmt.Errorf("SecureJoin: too many links") +var ErrSymlinkLoop = errors.Wrap(syscall.ELOOP, "secure join") // IsNotExist tells you if err is an error that implies that either the path // accessed does not exist (or path components don't exist). This is diff --git a/vendor/github.com/docker/go-units/README.md b/vendor/github.com/docker/go-units/README.md index f1d46d16..4f70a4e1 100644 --- a/vendor/github.com/docker/go-units/README.md +++ b/vendor/github.com/docker/go-units/README.md @@ -1,3 +1,5 @@ +[![GoDoc](https://godoc.org/github.com/docker/go-units?status.svg)](https://godoc.org/github.com/docker/go-units) + # Introduction go-units is a library to transform human friendly measurements into machine friendly values. @@ -6,6 +8,9 @@ go-units is a library to transform human friendly measurements into machine frie See the [docs in godoc](https://godoc.org/github.com/docker/go-units) for examples and documentation. -## License +## Copyright and license -go-units is licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for the full license text. +Copyright © 2015 Docker, Inc. + +go-units is licensed under the Apache License, Version 2.0. +See [LICENSE](LICENSE) for the full text of the license. diff --git a/vendor/github.com/docker/go-units/duration.go b/vendor/github.com/docker/go-units/duration.go index c219a8a9..ba02af26 100644 --- a/vendor/github.com/docker/go-units/duration.go +++ b/vendor/github.com/docker/go-units/duration.go @@ -12,19 +12,21 @@ import ( func HumanDuration(d time.Duration) string { if seconds := int(d.Seconds()); seconds < 1 { return "Less than a second" + } else if seconds == 1 { + return "1 second" } else if seconds < 60 { return fmt.Sprintf("%d seconds", seconds) } else if minutes := int(d.Minutes()); minutes == 1 { return "About a minute" - } else if minutes < 60 { + } else if minutes < 46 { return fmt.Sprintf("%d minutes", minutes) - } else if hours := int(d.Hours()); hours == 1 { + } else if hours := int(d.Hours() + 0.5); hours == 1 { return "About an hour" } else if hours < 48 { return fmt.Sprintf("%d hours", hours) } else if hours < 24*7*2 { return fmt.Sprintf("%d days", hours/24) - } else if hours < 24*30*3 { + } else if hours < 24*30*2 { return fmt.Sprintf("%d weeks", hours/24/7) } else if hours < 24*365*2 { return fmt.Sprintf("%d months", hours/24/30) diff --git a/vendor/github.com/docker/go-units/size.go b/vendor/github.com/docker/go-units/size.go index 3b59daff..85f6ab07 100644 --- a/vendor/github.com/docker/go-units/size.go +++ b/vendor/github.com/docker/go-units/size.go @@ -31,33 +31,46 @@ type unitMap map[string]int64 var ( decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB} binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB} - sizeRegex = regexp.MustCompile(`^(\d+)([kKmMgGtTpP])?[bB]?$`) + sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[iI]?[bB]?$`) ) var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} -// CustomSize returns a human-readable approximation of a size -// using custom format. -func CustomSize(format string, size float64, base float64, _map []string) string { +func getSizeAndUnit(size float64, base float64, _map []string) (float64, string) { i := 0 - for size >= base { + unitsLimit := len(_map) - 1 + for size >= base && i < unitsLimit { size = size / base i++ } - return fmt.Sprintf(format, size, _map[i]) + return size, _map[i] +} + +// CustomSize returns a human-readable approximation of a size +// using custom format. +func CustomSize(format string, size float64, base float64, _map []string) string { + size, unit := getSizeAndUnit(size, base, _map) + return fmt.Sprintf(format, size, unit) +} + +// HumanSizeWithPrecision allows the size to be in any precision, +// instead of 4 digit precision used in units.HumanSize. +func HumanSizeWithPrecision(size float64, precision int) string { + size, unit := getSizeAndUnit(size, 1000.0, decimapAbbrs) + return fmt.Sprintf("%.*g%s", precision, size, unit) } // HumanSize returns a human-readable approximation of a size // capped at 4 valid numbers (eg. "2.746 MB", "796 KB"). func HumanSize(size float64) string { - return CustomSize("%.4g %s", size, 1000.0, decimapAbbrs) + return HumanSizeWithPrecision(size, 4) } // BytesSize returns a human-readable size in bytes, kibibytes, // mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB"). func BytesSize(size float64) string { - return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs) + return CustomSize("%.4g%s", size, 1024.0, binaryAbbrs) } // FromHumanSize returns an integer from a human-readable specification of a @@ -77,19 +90,19 @@ func RAMInBytes(size string) (int64, error) { // Parses the human-readable size string into the amount it represents. func parseSize(sizeStr string, uMap unitMap) (int64, error) { matches := sizeRegex.FindStringSubmatch(sizeStr) - if len(matches) != 3 { + if len(matches) != 4 { return -1, fmt.Errorf("invalid size: '%s'", sizeStr) } - size, err := strconv.ParseInt(matches[1], 10, 0) + size, err := strconv.ParseFloat(matches[1], 64) if err != nil { return -1, err } - unitPrefix := strings.ToLower(matches[2]) + unitPrefix := strings.ToLower(matches[3]) if mul, ok := uMap[unitPrefix]; ok { - size *= mul + size *= float64(mul) } - return size, nil + return int64(size), nil } diff --git a/vendor/github.com/docker/go-units/ulimit.go b/vendor/github.com/docker/go-units/ulimit.go new file mode 100644 index 00000000..5ac7fd82 --- /dev/null +++ b/vendor/github.com/docker/go-units/ulimit.go @@ -0,0 +1,118 @@ +package units + +import ( + "fmt" + "strconv" + "strings" +) + +// Ulimit is a human friendly version of Rlimit. +type Ulimit struct { + Name string + Hard int64 + Soft int64 +} + +// Rlimit specifies the resource limits, such as max open files. +type Rlimit struct { + Type int `json:"type,omitempty"` + Hard uint64 `json:"hard,omitempty"` + Soft uint64 `json:"soft,omitempty"` +} + +const ( + // magic numbers for making the syscall + // some of these are defined in the syscall package, but not all. + // Also since Windows client doesn't get access to the syscall package, need to + // define these here + rlimitAs = 9 + rlimitCore = 4 + rlimitCPU = 0 + rlimitData = 2 + rlimitFsize = 1 + rlimitLocks = 10 + rlimitMemlock = 8 + rlimitMsgqueue = 12 + rlimitNice = 13 + rlimitNofile = 7 + rlimitNproc = 6 + rlimitRss = 5 + rlimitRtprio = 14 + rlimitRttime = 15 + rlimitSigpending = 11 + rlimitStack = 3 +) + +var ulimitNameMapping = map[string]int{ + //"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container. + "core": rlimitCore, + "cpu": rlimitCPU, + "data": rlimitData, + "fsize": rlimitFsize, + "locks": rlimitLocks, + "memlock": rlimitMemlock, + "msgqueue": rlimitMsgqueue, + "nice": rlimitNice, + "nofile": rlimitNofile, + "nproc": rlimitNproc, + "rss": rlimitRss, + "rtprio": rlimitRtprio, + "rttime": rlimitRttime, + "sigpending": rlimitSigpending, + "stack": rlimitStack, +} + +// ParseUlimit parses and returns a Ulimit from the specified string. +func ParseUlimit(val string) (*Ulimit, error) { + parts := strings.SplitN(val, "=", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid ulimit argument: %s", val) + } + + if _, exists := ulimitNameMapping[parts[0]]; !exists { + return nil, fmt.Errorf("invalid ulimit type: %s", parts[0]) + } + + var ( + soft int64 + hard = &soft // default to soft in case no hard was set + temp int64 + err error + ) + switch limitVals := strings.Split(parts[1], ":"); len(limitVals) { + case 2: + temp, err = strconv.ParseInt(limitVals[1], 10, 64) + if err != nil { + return nil, err + } + hard = &temp + fallthrough + case 1: + soft, err = strconv.ParseInt(limitVals[0], 10, 64) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1]) + } + + if soft > *hard { + return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard) + } + + return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil +} + +// GetRlimit returns the RLimit corresponding to Ulimit. +func (u *Ulimit) GetRlimit() (*Rlimit, error) { + t, exists := ulimitNameMapping[u.Name] + if !exists { + return nil, fmt.Errorf("invalid ulimit name %s", u.Name) + } + + return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil +} + +func (u *Ulimit) String() string { + return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard) +} diff --git a/vendor/github.com/godbus/dbus/README.markdown b/vendor/github.com/godbus/dbus/README.markdown index 0a6e7e5b..fd296487 100644 --- a/vendor/github.com/godbus/dbus/README.markdown +++ b/vendor/github.com/godbus/dbus/README.markdown @@ -1,3 +1,5 @@ +[![Build Status](https://travis-ci.org/godbus/dbus.svg?branch=master)](https://travis-ci.org/godbus/dbus) + dbus ---- @@ -12,7 +14,7 @@ D-Bus message bus system. ### Installation -This packages requires Go 1.1. If you installed it and set up your GOPATH, just run: +This packages requires Go 1.7. If you installed it and set up your GOPATH, just run: ``` go get github.com/godbus/dbus @@ -29,6 +31,7 @@ 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. diff --git a/vendor/github.com/godbus/dbus/auth.go b/vendor/github.com/godbus/dbus/auth.go index 98017b69..b0dcb54e 100644 --- a/vendor/github.com/godbus/dbus/auth.go +++ b/vendor/github.com/godbus/dbus/auth.go @@ -116,7 +116,6 @@ func (conn *Conn) Auth(methods []Auth) error { return err } go conn.inWorker() - go conn.outWorker() return nil } } diff --git a/vendor/github.com/godbus/dbus/auth_anonymous.go b/vendor/github.com/godbus/dbus/auth_anonymous.go new file mode 100644 index 00000000..75f3ad34 --- /dev/null +++ b/vendor/github.com/godbus/dbus/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/call.go b/vendor/github.com/godbus/dbus/call.go index ba6e73f6..2cb18901 100644 --- a/vendor/github.com/godbus/dbus/call.go +++ b/vendor/github.com/godbus/dbus/call.go @@ -1,9 +1,12 @@ 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 @@ -20,9 +23,25 @@ type Call struct { // Holds the response once the call is done. Body []interface{} + + // tracks context and canceler + ctx context.Context + ctxCanceler context.CancelFunc } -var errSignature = errors.New("dbus: mismatched signature") +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 @@ -34,3 +53,8 @@ func (c *Call) Store(retvalues ...interface{}) error { return Store(c.Body, retvalues...) } + +func (c *Call) done() { + c.Done <- c + c.ContextCancel() +} diff --git a/vendor/github.com/godbus/dbus/conn.go b/vendor/github.com/godbus/dbus/conn.go index a4f53940..b38920ba 100644 --- a/vendor/github.com/godbus/dbus/conn.go +++ b/vendor/github.com/godbus/dbus/conn.go @@ -1,6 +1,7 @@ package dbus import ( + "context" "errors" "io" "os" @@ -9,8 +10,6 @@ import ( "sync" ) -const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" - var ( systemBus *Conn systemBusLck sync.Mutex @@ -36,25 +35,13 @@ type Conn struct { unixFD bool uuid string - names []string - namesLck sync.RWMutex + handler Handler + signalHandler SignalHandler + serialGen SerialGenerator - serialLck sync.Mutex - nextSerial uint32 - serialUsed map[uint32]bool - - calls map[uint32]*Call - callsLck sync.RWMutex - - handlers map[ObjectPath]map[string]exportWithMapping - handlersLck sync.RWMutex - - out chan *Message - closed bool - outLck sync.RWMutex - - signals []chan<- *Signal - signalsLck sync.Mutex + names *nameTracker + calls *callTracker + outHandler *outputHandler eavesdropped chan<- *Message eavesdroppedLck sync.Mutex @@ -89,14 +76,32 @@ func SessionBus() (conn *Conn, err error) { 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() (*Conn, error) { - address := os.Getenv("DBUS_SESSION_BUS_ADDRESS") - if address != "" && address != "autolaunch:" { - return Dial(address) +func SessionBusPrivate(opts ...ConnOption) (*Conn, error) { + address, err := getSessionBusAddress() + if err != nil { + return nil, err } - return sessionBusPlatform() + 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 @@ -129,37 +134,93 @@ func SystemBus() (conn *Conn, err error) { } // SystemBusPrivate returns a new private connection to the system bus. -func SystemBusPrivate() (*Conn, error) { - address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") - if address != "" { - return Dial(address) - } - return Dial(defaultSystemBusAddress) +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) (*Conn, error) { +func Dial(address string, opts ...ConnOption) (*Conn, error) { tr, err := getTransport(address) if err != nil { return nil, err } - return newConn(tr) + 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 + } } // NewConn creates a new private *Conn from an already established connection. -func NewConn(conn io.ReadWriteCloser) (*Conn, error) { - return newConn(genericTransport{conn}) +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) (*Conn, error) { +func newConn(tr transport, opts ...ConnOption) (*Conn, error) { conn := new(Conn) conn.transport = tr - conn.calls = make(map[uint32]*Call) - conn.out = make(chan *Message, 10) - conn.handlers = make(map[ObjectPath]map[string]exportWithMapping) - conn.nextSerial = 1 - conn.serialUsed = map[uint32]bool{0: true} + for _, opt := range opts { + if err := opt(conn); err != nil { + return nil, err + } + } + 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 } @@ -174,27 +235,21 @@ func (conn *Conn) BusObject() BusObject { // 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.outLck.Lock() - if conn.closed { - // inWorker calls Close on read error, the read error may - // be caused by another caller calling Close to shutdown the - // dbus connection, a double-close scenario we prevent here. - conn.outLck.Unlock() - return nil + conn.outHandler.close() + if term, ok := conn.signalHandler.(Terminator); ok { + term.Terminate() } - close(conn.out) - conn.closed = true - conn.outLck.Unlock() - conn.signalsLck.Lock() - for _, ch := range conn.signals { - close(ch) + + if term, ok := conn.handler.(Terminator); ok { + term.Terminate() } - conn.signalsLck.Unlock() + conn.eavesdroppedLck.Lock() if conn.eavesdropped != nil { close(conn.eavesdropped) } conn.eavesdroppedLck.Unlock() + return conn.transport.Close() } @@ -212,17 +267,9 @@ func (conn *Conn) Eavesdrop(ch chan<- *Message) { conn.eavesdroppedLck.Unlock() } -// getSerial returns an unused serial. +// GetSerial returns an unused serial. func (conn *Conn) getSerial() uint32 { - conn.serialLck.Lock() - defer conn.serialLck.Unlock() - n := conn.nextSerial - for conn.serialUsed[n] { - n++ - } - conn.serialUsed[n] = true - conn.nextSerial = n + 1 - return n + return conn.serialGen.GetSerial() } // Hello sends the initial org.freedesktop.DBus.Hello call. This method must be @@ -234,10 +281,7 @@ func (conn *Conn) Hello() error { if err != nil { return err } - conn.namesLck.Lock() - conn.names = make([]string, 1) - conn.names[0] = s - conn.namesLck.Unlock() + conn.names.acquireUniqueConnectionName(s) return nil } @@ -246,132 +290,90 @@ func (conn *Conn) Hello() error { func (conn *Conn) inWorker() { for { msg, err := conn.ReadMessage() - if err == nil { - conn.eavesdroppedLck.Lock() - if conn.eavesdropped != nil { - select { - case conn.eavesdropped <- msg: - default: - } - conn.eavesdroppedLck.Unlock() - continue + if err != nil { + if _, ok := err.(InvalidMessageError); !ok { + // Some read error occured (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() - dest, _ := msg.Headers[FieldDestination].value.(string) - found := false - if dest == "" { - found = true - } else { - conn.namesLck.RLock() - if len(conn.names) == 0 { - found = true - } - for _, v := range conn.names { - if dest == v { - found = true - break - } - } - conn.namesLck.RUnlock() - } - if !found { - // Eavesdropped a message, but no channel for it is registered. - // Ignore it. - continue - } - switch msg.Type { - case TypeMethodReply, TypeError: - serial := msg.Headers[FieldReplySerial].value.(uint32) - conn.callsLck.Lock() - if c, ok := conn.calls[serial]; ok { - if msg.Type == TypeError { - name, _ := msg.Headers[FieldErrorName].value.(string) - c.Err = Error{name, msg.Body} - } else { - c.Body = msg.Body - } - c.Done <- c - conn.serialLck.Lock() - delete(conn.serialUsed, serial) - conn.serialLck.Unlock() - delete(conn.calls, serial) - } - conn.callsLck.Unlock() - case TypeSignal: - 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.namesLck.Lock() - for i, v := range conn.names { - if v == name { - conn.names = append(conn.names[:i], - conn.names[i+1:]...) - } - } - conn.namesLck.Unlock() - } 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.namesLck.Lock() - conn.names = append(conn.names, name) - conn.namesLck.Unlock() - } - } - signal := &Signal{ - Sender: sender, - Path: msg.Headers[FieldPath].value.(ObjectPath), - Name: iface + "." + member, - Body: msg.Body, - } - conn.signalsLck.Lock() - for _, ch := range conn.signals { - ch <- signal - } - conn.signalsLck.Unlock() - case TypeMethodCall: - go conn.handleCall(msg) - } - } else if _, ok := err.(InvalidMessageError); !ok { - // Some read error occured (usually EOF); we can't really do - // anything but to shut down all stuff and returns errors to all - // pending replies. - conn.Close() - conn.callsLck.RLock() - for _, v := range conn.calls { - v.Err = err - v.Done <- v - } - conn.callsLck.RUnlock() - return + continue } - // invalid messages are ignored + 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 + } + 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 { - conn.namesLck.RLock() - // copy the slice so it can't be modified - s := make([]string, len(conn.names)) - copy(s, conn.names) - conn.namesLck.RUnlock() - return s + return conn.names.listKnownNames() } // Object returns the object identified by the given destination name and path. @@ -381,24 +383,17 @@ func (conn *Conn) Object(dest string, path ObjectPath) BusObject { // outWorker runs in an own goroutine, encoding and sending messages that are // sent to conn.out. -func (conn *Conn) outWorker() { - for msg := range conn.out { - err := conn.SendMessage(msg) - conn.callsLck.RLock() - if err != nil { - if c := conn.calls[msg.serial]; c != nil { - c.Err = err - c.Done <- c - } - conn.serialLck.Lock() - delete(conn.serialUsed, msg.serial) - conn.serialLck.Unlock() - } else if msg.Type != TypeMethodCall { - conn.serialLck.Lock() - delete(conn.serialUsed, msg.serial) - conn.serialLck.Unlock() - } - conn.callsLck.RUnlock() +func (conn *Conn) sendMessage(msg *Message) { + conn.sendMessageAndIfClosed(msg, func() {}) +} + +func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) { + 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) } } @@ -409,8 +404,21 @@ func (conn *Conn) outWorker() { // 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 { - var 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 { @@ -426,33 +434,42 @@ func (conn *Conn) Send(msg *Message, ch chan *Call) *Call { call.Method = iface + "." + member call.Args = msg.Body call.Done = ch - conn.callsLck.Lock() - conn.calls[msg.serial] = call - conn.callsLck.Unlock() - conn.outLck.RLock() - if conn.closed { - call.Err = ErrClosed - call.Done <- call - } else { - conn.out <- msg - } - conn.outLck.RUnlock() + 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 { - conn.outLck.RLock() - if conn.closed { + canceler() + call = &Call{Err: nil} + conn.sendMessageAndIfClosed(msg, func() { call = &Call{Err: ErrClosed} - } else { - conn.out <- msg - call = &Call{Err: nil} - } - conn.outLck.RUnlock() + }) } return call } // sendError creates an error message corresponding to the parameters and sends // it to conn.out. -func (conn *Conn) sendError(e Error, dest string, serial uint32) { +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() @@ -466,11 +483,7 @@ func (conn *Conn) sendError(e Error, dest string, serial uint32) { if len(e.Body) > 0 { msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...)) } - conn.outLck.RLock() - if !conn.closed { - conn.out <- msg - } - conn.outLck.RUnlock() + conn.sendMessage(msg) } // sendReply creates a method reply message corresponding to the parameters and @@ -488,28 +501,33 @@ func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) { if len(values) > 0 { msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) } - conn.outLck.RLock() - if !conn.closed { - conn.out <- msg + conn.sendMessage(msg) +} + +func (conn *Conn) defaultSignalAction(fn func(h *defaultSignalHandler, ch chan<- *Signal), ch chan<- *Signal) { + if !isDefaultSignalHandler(conn.signalHandler) { + return } - conn.outLck.RUnlock() + handler := conn.signalHandler.(*defaultSignalHandler) + fn(handler, ch) } // Signal registers the given channel to be passed all received signal messages. // The caller has to make sure that ch is sufficiently buffered; if a message // arrives when a write to c is not possible, it is discarded. // -// Multiple of these channels can be registered at the same time. Passing a -// channel that already is registered will remove it from the list of the -// registered channels. +// 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. func (conn *Conn) Signal(ch chan<- *Signal) { - conn.signalsLck.Lock() - conn.signals = append(conn.signals, ch) - conn.signalsLck.Unlock() + conn.defaultSignalAction((*defaultSignalHandler).addSignal, ch) +} + +// RemoveSignal removes the given channel from the list of the registered channels. +func (conn *Conn) RemoveSignal(ch chan<- *Signal) { + conn.defaultSignalAction((*defaultSignalHandler).removeSignal, ch) } // SupportsUnixFDs returns whether the underlying transport supports passing of @@ -610,16 +628,220 @@ func dereferenceAll(vs []interface{}) []interface{} { // getKey gets a key from a the list of keys. Returns "" on error / not found... func getKey(s, key string) string { - i := strings.Index(s, key) - if i == -1 { - return "" + 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 { + 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() + } + return +} + +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() + } + return +} + +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() + } + return +} + +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() } - if i+len(key)+1 >= len(s) || s[i+len(key)] != '=' { - return "" - } - j := strings.Index(s, ",") - if j == -1 { - j = len(s) - } - return s[i+len(key)+1 : j] } diff --git a/vendor/github.com/godbus/dbus/conn_darwin.go b/vendor/github.com/godbus/dbus/conn_darwin.go index b67bb1b8..6e2e4020 100644 --- a/vendor/github.com/godbus/dbus/conn_darwin.go +++ b/vendor/github.com/godbus/dbus/conn_darwin.go @@ -2,20 +2,36 @@ package dbus import ( "errors" + "fmt" + "os" "os/exec" ) -func sessionBusPlatform() (*Conn, error) { +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 nil, err + return "", err } if len(b) == 0 { - return nil, errors.New("dbus: couldn't determine address of session bus") + return "", errors.New("dbus: couldn't determine address of session bus") } - return Dial("unix:path=" + string(b[:len(b)-1])) + 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/conn_other.go b/vendor/github.com/godbus/dbus/conn_other.go index f74b8758..289044a4 100644 --- a/vendor/github.com/godbus/dbus/conn_other.go +++ b/vendor/github.com/godbus/dbus/conn_other.go @@ -5,23 +5,87 @@ package dbus import ( "bytes" "errors" + "fmt" + "io/ioutil" + "os" "os/exec" + "os/user" + "path" + "strings" ) -func sessionBusPlatform() (*Conn, error) { +func getSessionBusPlatformAddress() (string, error) { cmd := exec.Command("dbus-launch") b, err := cmd.CombinedOutput() if err != nil { - return nil, err + return "", err } i := bytes.IndexByte(b, '=') j := bytes.IndexByte(b, '\n') if i == -1 || j == -1 { - return nil, errors.New("dbus: couldn't determine address of session bus") + return "", errors.New("dbus: couldn't determine address of session bus") } - return Dial(string(b[i+1 : j])) + 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//bus if this exists, it *is* the bus socket. present on +// Ubuntu 18.04 +// * /run/user//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//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//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/conn_unix.go b/vendor/github.com/godbus/dbus/conn_unix.go new file mode 100644 index 00000000..4cba8ae8 --- /dev/null +++ b/vendor/github.com/godbus/dbus/conn_unix.go @@ -0,0 +1,18 @@ +//+build !windows,!solaris,!darwin + +package dbus + +import ( + "os" + "fmt" +) + +const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" + +func getSystemBusPlatformAddress() string { + address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") + if address != "" { + return fmt.Sprintf("unix:path=%s", address) + } + return defaultSystemBusAddress +} \ No newline at end of file diff --git a/vendor/github.com/godbus/dbus/conn_windows.go b/vendor/github.com/godbus/dbus/conn_windows.go new file mode 100644 index 00000000..4291e451 --- /dev/null +++ b/vendor/github.com/godbus/dbus/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/dbus.go b/vendor/github.com/godbus/dbus/dbus.go index 2ce68735..c6d0d3ce 100644 --- a/vendor/github.com/godbus/dbus/dbus.go +++ b/vendor/github.com/godbus/dbus/dbus.go @@ -2,6 +2,7 @@ package dbus import ( "errors" + "fmt" "reflect" "strings" ) @@ -12,6 +13,8 @@ var ( 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)) @@ -22,6 +25,7 @@ var ( 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)) ) @@ -46,86 +50,251 @@ func Store(src []interface{}, dest ...interface{}) error { } for i := range src { - if err := store(src[i], dest[i]); err != nil { + if err := storeInterfaces(src[i], dest[i]); err != nil { return err } } return nil } -func store(src, dest interface{}) error { - if reflect.TypeOf(dest).Elem() == reflect.TypeOf(src) { - reflect.ValueOf(dest).Elem().Set(reflect.ValueOf(src)) - return nil - } else if hasStruct(dest) { - rv := reflect.ValueOf(dest).Elem() - switch rv.Kind() { - case reflect.Struct: - vs, ok := src.([]interface{}) - if !ok { - return errors.New("dbus.Store: type mismatch") - } - t := rv.Type() - ndest := make([]interface{}, 0, rv.NumField()) - for i := 0; i < rv.NumField(); i++ { - field := t.Field(i) - if field.PkgPath == "" && field.Tag.Get("dbus") != "-" { - ndest = append(ndest, rv.Field(i).Addr().Interface()) - } - } - if len(vs) != len(ndest) { - return errors.New("dbus.Store: type mismatch") - } - err := Store(vs, ndest...) - if err != nil { - return errors.New("dbus.Store: type mismatch") - } - case reflect.Slice: - sv := reflect.ValueOf(src) - if sv.Kind() != reflect.Slice { - return errors.New("dbus.Store: type mismatch") - } - rv.Set(reflect.MakeSlice(rv.Type(), sv.Len(), sv.Len())) - for i := 0; i < sv.Len(); i++ { - if err := store(sv.Index(i).Interface(), rv.Index(i).Addr().Interface()); err != nil { - return err - } - } - case reflect.Map: - sv := reflect.ValueOf(src) - if sv.Kind() != reflect.Map { - return errors.New("dbus.Store: type mismatch") - } - keys := sv.MapKeys() - rv.Set(reflect.MakeMap(sv.Type())) - for _, key := range keys { - v := reflect.New(sv.Type().Elem()) - if err := store(v, sv.MapIndex(key).Interface()); err != nil { - return err - } - rv.SetMapIndex(key, v.Elem()) - } - default: - return errors.New("dbus.Store: type mismatch") - } - return nil - } else { - return errors.New("dbus.Store: type mismatch") +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 hasStruct(v interface{}) bool { - t := reflect.TypeOf(v) - for { - switch t.Kind() { - case reflect.Struct: - return true - case reflect.Slice, reflect.Ptr, reflect.Map: - t = t.Elem() - default: - return false +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) + } + 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. @@ -177,15 +346,15 @@ func alignment(t reflect.Type) int { return 4 case signatureType: return 1 - case interfacesType: // sometimes used for structs - return 8 + case interfacesType: + return 4 } switch t.Kind() { case reflect.Uint8: return 1 case reflect.Uint16, reflect.Int16: return 2 - case reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map: + 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 @@ -200,7 +369,7 @@ 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.String, reflect.Uint, reflect.Int: return true } diff --git a/vendor/github.com/godbus/dbus/decoder.go b/vendor/github.com/godbus/dbus/decoder.go index ef50dcab..5c27d3b5 100644 --- a/vendor/github.com/godbus/dbus/decoder.go +++ b/vendor/github.com/godbus/dbus/decoder.go @@ -191,7 +191,14 @@ func (dec *decoder) decode(s string, depth int) interface{} { length := dec.decode("u", depth).(uint32) v := reflect.MakeSlice(reflect.SliceOf(typeFor(s[1:])), 0, int(length)) // Even for empty arrays, the correct padding must be included - dec.align(alignment(typeFor(s[1:]))) + 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) diff --git a/vendor/github.com/godbus/dbus/default_handler.go b/vendor/github.com/godbus/dbus/default_handler.go new file mode 100644 index 00000000..81dbcc7e --- /dev/null +++ b/vendor/github.com/godbus/dbus/default_handler.go @@ -0,0 +1,321 @@ +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("") + 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") + } + xml.WriteString("\n") + 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{ + closeChan: make(chan struct{}), + } +} + +func isDefaultSignalHandler(handler SignalHandler) bool { + _, ok := handler.(*defaultSignalHandler) + return ok +} + +type defaultSignalHandler struct { + sync.RWMutex + closed bool + signals []chan<- *Signal + closeChan chan struct{} +} + +func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) { + sh.RLock() + defer sh.RUnlock() + if sh.closed { + return + } + for _, ch := range sh.signals { + select { + case ch <- signal: + case <-sh.closeChan: + return + default: + go func() { + select { + case ch <- signal: + case <-sh.closeChan: + return + } + }() + } + } +} + +func (sh *defaultSignalHandler) Init() error { + sh.Lock() + sh.signals = make([]chan<- *Signal, 0) + sh.closeChan = make(chan struct{}) + sh.Unlock() + return nil +} + +func (sh *defaultSignalHandler) Terminate() { + sh.Lock() + if !sh.closed { + close(sh.closeChan) + } + sh.closed = true + for _, ch := range sh.signals { + close(ch) + } + sh.signals = nil + sh.Unlock() +} + +func (sh *defaultSignalHandler) addSignal(ch chan<- *Signal) { + sh.Lock() + defer sh.Unlock() + if sh.closed { + return + } + sh.signals = append(sh.signals, ch) + +} + +func (sh *defaultSignalHandler) removeSignal(ch chan<- *Signal) { + sh.Lock() + defer sh.Unlock() + if sh.closed { + return + } + for i := len(sh.signals) - 1; i >= 0; i-- { + if ch == sh.signals[i] { + copy(sh.signals[i:], sh.signals[i+1:]) + sh.signals[len(sh.signals)-1] = nil + sh.signals = sh.signals[:len(sh.signals)-1] + } + } +} diff --git a/vendor/github.com/godbus/dbus/doc.go b/vendor/github.com/godbus/dbus/doc.go index deff554a..895036a8 100644 --- a/vendor/github.com/godbus/dbus/doc.go +++ b/vendor/github.com/godbus/dbus/doc.go @@ -19,6 +19,8 @@ respective D-Bus equivalents: bool | BOOLEAN int16 | INT16 uint16 | UINT16 + int | INT32 + uint | UINT32 int32 | INT32 uint32 | UINT32 int64 | INT64 @@ -28,6 +30,7 @@ respective D-Bus equivalents: ObjectPath | OBJECT_PATH Signature | SIGNATURE Variant | VARIANT + interface{} | VARIANT UnixFDIndex | UNIX_FD Slices and arrays encode as ARRAYs of their element type. @@ -41,6 +44,9 @@ 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. diff --git a/vendor/github.com/godbus/dbus/encoder.go b/vendor/github.com/godbus/dbus/encoder.go index 9f0a9e89..8bb71776 100644 --- a/vendor/github.com/godbus/dbus/encoder.go +++ b/vendor/github.com/godbus/dbus/encoder.go @@ -96,10 +96,10 @@ func (enc *encoder) encode(v reflect.Value, depth int) { case reflect.Uint16: enc.binwrite(uint16(v.Uint())) enc.pos += 2 - case reflect.Int32: + case reflect.Int, reflect.Int32: enc.binwrite(int32(v.Int())) enc.pos += 4 - case reflect.Uint32: + case reflect.Uint, reflect.Uint32: enc.binwrite(uint32(v.Uint())) enc.pos += 4 case reflect.Int64: @@ -202,6 +202,8 @@ func (enc *encoder) encode(v reflect.Value, depth int) { 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/export.go b/vendor/github.com/godbus/dbus/export.go index c6440a74..95d0e295 100644 --- a/vendor/github.com/godbus/dbus/export.go +++ b/vendor/github.com/godbus/dbus/export.go @@ -8,167 +8,73 @@ import ( ) var ( - errmsgInvalidArg = Error{ + ErrMsgInvalidArg = Error{ "org.freedesktop.DBus.Error.InvalidArgs", []interface{}{"Invalid type / number of args"}, } - errmsgNoObject = Error{ + ErrMsgNoObject = Error{ "org.freedesktop.DBus.Error.NoSuchObject", []interface{}{"No such object"}, } - errmsgUnknownMethod = Error{ + 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"}, + } ) -// exportWithMapping represents an exported struct along with a method name -// mapping to allow for exporting lower-case methods, etc. -type exportWithMapping struct { - export interface{} - - // Method name mapping; key -> struct method, value -> dbus method. - mapping map[string]string - - // Whether or not this export is for the entire subtree - includeSubtree bool +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 exportedMethod(export exportWithMapping, name string) reflect.Value { - if export.export == nil { - return reflect.Value{} - } - - // If a mapping was included in the export, check the map to see if we - // should be looking for a different method in the export. - if export.mapping != nil { - for key, value := range export.mapping { - if value == name { - name = key - break - } - - // Catch the case where a method is aliased but the client is calling - // the original, e.g. the "Foo" method was exported mapped to - // "foo," and dbus client called the original "Foo." - if key == name { - return reflect.Value{} - } - } - } - - value := reflect.ValueOf(export.export) - m := value.MethodByName(name) - - // Catch the case of attempting to call an unexported method - method, ok := value.Type().MethodByName(name) - - if !m.IsValid() || !ok || method.PkgPath != "" { - return reflect.Value{} - } - t := m.Type() - if t.NumOut() == 0 || - t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) { - - return reflect.Value{} - } - return m -} - -// searchHandlers will look through all registered handlers looking for one -// to handle the given path. If a verbatim one isn't found, it will check for -// a subtree registration for the path as well. -func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportWithMapping, bool) { - conn.handlersLck.RLock() - defer conn.handlersLck.RUnlock() - - handlers, ok := conn.handlers[path] +func computeMethodName(name string, mapping map[string]string) string { + newname, ok := mapping[name] if ok { - return handlers, ok + name = newname } - - // If handlers weren't found for this exact path, look for a matching subtree - // registration - handlers = make(map[string]exportWithMapping) - path = path[:strings.LastIndex(string(path), "/")] - for len(path) > 0 { - var subtreeHandlers map[string]exportWithMapping - subtreeHandlers, ok = conn.handlers[path] - if ok { - for iface, handler := range subtreeHandlers { - // Only include this handler if it registered for the subtree - if handler.includeSubtree { - handlers[iface] = handler - } - } - - break - } - - path = path[:strings.LastIndex(string(path), "/")] - } - - return handlers, ok + return name } -// 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, hasIface := 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) +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 } - return - } - if len(name) == 0 { - conn.sendError(errmsgUnknownMethod, sender, serial) + // map names while building table + methods[computeMethodName(methtype.Name, mapping)] = method } + return methods +} - // Find the exported handler (if any) for this path - handlers, ok := conn.searchHandlers(path) - if !ok { - conn.sendError(errmsgNoObject, sender, serial) - return - } +func standardMethodArgumentDecode(m Method, sender string, msg *Message, body []interface{}) ([]interface{}, error) { + pointers := make([]interface{}, m.NumArguments()) + decode := make([]interface{}, 0, len(body)) - var m reflect.Value - if hasIface { - iface := handlers[ifaceName] - m = exportedMethod(iface, name) - } else { - for _, v := range handlers { - m = exportedMethod(v, name) - if m.IsValid() { - break - } - } - } - - if !m.IsValid() { - conn.sendError(errmsgUnknownMethod, sender, serial) - return - } - - t := m.Type() - vs := msg.Body - pointers := make([]interface{}, t.NumIn()) - decode := make([]interface{}, 0, len(vs)) - for i := 0; i < t.NumIn(); i++ { - tp := t.In(i) + 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() { @@ -180,26 +86,73 @@ func (conn *Conn) handleCall(msg *Message) { } } - if len(decode) != len(vs) { - conn.sendError(errmsgInvalidArg, sender, serial) + 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 } - if err := Store(vs, decode...); err != nil { - conn.sendError(errmsgInvalidArg, sender, serial) + iface, exists := object.LookupInterface(ifaceName) + if !exists { + conn.sendError(ErrMsgUnknownInterface, sender, serial) return } - // Extract parameters - params := make([]reflect.Value, len(pointers)) - for i := 0; i < len(pointers); i++ { - params[i] = reflect.ValueOf(pointers[i]).Elem() + 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 } - // Call method - ret := m.Call(params) - if em := ret[t.NumOut()-1].Interface().(*Error); em != nil { - conn.sendError(*em, sender, serial) + ret, err := m.Call(args...) + if err != nil { + conn.sendError(err, sender, serial) return } @@ -212,18 +165,13 @@ func (conn *Conn) handleCall(msg *Message) { reply.Headers[FieldDestination] = msg.Headers[FieldSender] } reply.Headers[FieldReplySerial] = MakeVariant(msg.serial) - reply.Body = make([]interface{}, len(ret)-1) - for i := 0; i < len(ret)-1; i++ { - reply.Body[i] = ret[i].Interface() + reply.Body = make([]interface{}, len(ret)) + for i := 0; i < len(ret); i++ { + reply.Body[i] = ret[i] } - if len(ret) != 1 { - reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...)) - } - conn.outLck.RLock() - if !conn.closed { - conn.out <- reply - } - conn.outLck.RUnlock() + reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...)) + + conn.sendMessage(reply) } } @@ -256,12 +204,14 @@ func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) erro if len(values) > 0 { msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) } - conn.outLck.RLock() - defer conn.outLck.RUnlock() - if conn.closed { + + var closed bool + conn.sendMessageAndIfClosed(msg, func() { + closed = true + }) + if closed { return ErrClosed } - conn.out <- msg return nil } @@ -303,7 +253,7 @@ func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error { // 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.exportWithMap(v, mapping, path, iface, false) + return conn.export(getMethods(v, mapping), path, iface, false) } // ExportSubtree works exactly like Export but registers the given value for @@ -326,38 +276,89 @@ func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) er // 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.exportWithMap(v, mapping, path, iface, true) + 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) exportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string, includeSubtree bool) error { +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) } - conn.handlersLck.Lock() - defer conn.handlersLck.Unlock() - // Remove a previous export if the interface is nil - if v == nil { - if _, ok := conn.handlers[path]; ok { - delete(conn.handlers[path], iface) - if len(conn.handlers[path]) == 0 { - delete(conn.handlers, path) - } - } - - return 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 _, ok := conn.handlers[path]; !ok { - conn.handlers[path] = make(map[string]exportWithMapping) + 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 - conn.handlers[path][iface] = exportWithMapping{export: v, mapping: mapping, includeSubtree: includeSubtree} + obj := h.objects[path] + obj.AddInterface(iface, newExportedIntf(exportedMethods, includeSubtree)) return nil } diff --git a/vendor/github.com/godbus/dbus/go.mod b/vendor/github.com/godbus/dbus/go.mod new file mode 100644 index 00000000..bdcd1259 --- /dev/null +++ b/vendor/github.com/godbus/dbus/go.mod @@ -0,0 +1 @@ +module github.com/godbus/dbus diff --git a/vendor/github.com/godbus/dbus/message.go b/vendor/github.com/godbus/dbus/message.go index 075d6e38..6a925367 100644 --- a/vendor/github.com/godbus/dbus/message.go +++ b/vendor/github.com/godbus/dbus/message.go @@ -22,6 +22,13 @@ const ( // 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. @@ -248,7 +255,7 @@ func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) error { // 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) != 0 { + if msg.Flags & ^(FlagNoAutoStart|FlagNoReplyExpected|FlagAllowInteractiveAuthorization) != 0 { return InvalidMessageError("invalid flags") } if msg.Type == 0 || msg.Type >= typeMax { diff --git a/vendor/github.com/godbus/dbus/object.go b/vendor/github.com/godbus/dbus/object.go index 7ef45da4..f27ffe14 100644 --- a/vendor/github.com/godbus/dbus/object.go +++ b/vendor/github.com/godbus/dbus/object.go @@ -1,6 +1,7 @@ package dbus import ( + "context" "errors" "strings" ) @@ -9,7 +10,11 @@ import ( // 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) Destination() string Path() ObjectPath @@ -24,7 +29,74 @@ type Object struct { // 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.Go(method, flags, make(chan *Call, 1), args...).Done + 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 +} + +// 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 +} + +// WithMatchOption creates match option with given key and value +func WithMatchOption(key, value string) MatchOption { + return MatchOption{key, value} +} + +// WithMatchObjectPath creates match option that filters events based on given path +func WithMatchObjectPath(path ObjectPath) MatchOption { + return MatchOption{"path", string(path)} +} + +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, ",") +} + +// 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. +func (o *Object) AddMatchSignal(iface, member string, options ...MatchOption) *Call { + base := []MatchOption{ + {"type", "signal"}, + {"interface", iface}, + {"member", 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 +func (o *Object) RemoveMatchSignal(iface, member string, options ...MatchOption) *Call { + base := []MatchOption{ + {"type", "signal"}, + {"interface", iface}, + {"member", 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 @@ -33,11 +105,24 @@ func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call { // 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 of which only the Err member is valid. +// 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 { @@ -65,33 +150,41 @@ func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface } 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.callsLck.Lock() - o.conn.calls[msg.serial] = call - o.conn.callsLck.Unlock() - o.conn.outLck.RLock() - if o.conn.closed { - call.Err = ErrClosed - call.Done <- call - } else { - o.conn.out <- msg - } - o.conn.outLck.RUnlock() + 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 } - o.conn.outLck.RLock() - defer o.conn.outLck.RUnlock() - if o.conn.closed { - return &Call{Err: ErrClosed} + done := make(chan *Call, 1) + call := &Call{ + Err: nil, + Done: done, } - o.conn.out <- msg - return &Call{Err: nil} + defer func() { + call.Done <- call + close(done) + }() + o.conn.sendMessageAndIfClosed(msg, func() { + call.Err = ErrClosed + }) + return call } // GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given @@ -115,12 +208,12 @@ func (o *Object) GetProperty(p string) (Variant, error) { return result, nil } -// Destination returns the destination that calls on o are sent to. +// 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 are sent to. +// 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/server_interfaces.go b/vendor/github.com/godbus/dbus/server_interfaces.go new file mode 100644 index 00000000..01166f0b --- /dev/null +++ b/vendor/github.com/godbus/dbus/server_interfaces.go @@ -0,0 +1,99 @@ +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) +} + +// 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/sig.go b/vendor/github.com/godbus/dbus/sig.go index f45b53ce..c1b80920 100644 --- a/vendor/github.com/godbus/dbus/sig.go +++ b/vendor/github.com/godbus/dbus/sig.go @@ -57,12 +57,12 @@ func getSignature(t reflect.Type) string { return "n" case reflect.Uint16: return "q" - case reflect.Int32: + case reflect.Int, reflect.Int32: if t == unixFDType { return "h" } return "i" - case reflect.Uint32: + case reflect.Uint, reflect.Uint32: if t == unixFDIndexType { return "h" } @@ -101,6 +101,8 @@ func getSignature(t reflect.Type) string { panic(InvalidTypeError{t}) } return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}" + case reflect.Interface: + return "v" } panic(InvalidTypeError{t}) } @@ -162,7 +164,7 @@ 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 successfull, err is nil +// 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. diff --git a/vendor/github.com/godbus/dbus/transport_generic.go b/vendor/github.com/godbus/dbus/transport_generic.go index 46f8f49d..718a1ff0 100644 --- a/vendor/github.com/godbus/dbus/transport_generic.go +++ b/vendor/github.com/godbus/dbus/transport_generic.go @@ -4,8 +4,23 @@ 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 } @@ -31,5 +46,5 @@ func (t genericTransport) SendMessage(msg *Message) error { return errors.New("dbus: unix fd passing not enabled") } } - return msg.EncodeTo(t, binary.LittleEndian) + return msg.EncodeTo(t, nativeEndian) } diff --git a/vendor/github.com/godbus/dbus/transport_nonce_tcp.go b/vendor/github.com/godbus/dbus/transport_nonce_tcp.go new file mode 100644 index 00000000..697739ef --- /dev/null +++ b/vendor/github.com/godbus/dbus/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/transport_tcp.go b/vendor/github.com/godbus/dbus/transport_tcp.go new file mode 100644 index 00000000..dd1c8e59 --- /dev/null +++ b/vendor/github.com/godbus/dbus/transport_tcp.go @@ -0,0 +1,43 @@ +//+build !windows + +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/transport_unix.go b/vendor/github.com/godbus/dbus/transport_unix.go index 3fafeabb..f000c6b5 100644 --- a/vendor/github.com/godbus/dbus/transport_unix.go +++ b/vendor/github.com/godbus/dbus/transport_unix.go @@ -1,4 +1,4 @@ -//+build !windows +//+build !windows,!solaris package dbus @@ -31,6 +31,7 @@ func (o *oobReader) Read(b []byte) (n int, err error) { type unixTransport struct { *net.UnixConn + rdr *oobReader hasUnixFDs bool } @@ -79,10 +80,15 @@ func (t *unixTransport) ReadMessage() (*Message, error) { // 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. - rd := &oobReader{conn: t.UnixConn} + 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(rd, csheader[:]); err != nil { + if _, err := io.ReadFull(t.rdr, csheader[:]); err != nil { return nil, err } switch csheader[0] { @@ -104,7 +110,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { // decode headers and look for unix fds headerdata := make([]byte, hlen+4) copy(headerdata, csheader[12:]) - if _, err := io.ReadFull(t, headerdata[4:]); err != nil { + if _, err := io.ReadFull(t.rdr, headerdata[4:]); err != nil { return nil, err } dec := newDecoder(bytes.NewBuffer(headerdata), order) @@ -122,7 +128,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { all := make([]byte, 16+hlen+blen) copy(all, csheader[:]) copy(all[16:], headerdata[4:]) - if _, err := io.ReadFull(rd, all[16+hlen:]); err != nil { + if _, err := io.ReadFull(t.rdr, all[16+hlen:]); err != nil { return nil, err } if unixfds != 0 { @@ -130,7 +136,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { return nil, errors.New("dbus: got unix fds on unsupported transport") } // read the fds from the OOB data - scms, err := syscall.ParseSocketControlMessage(rd.oob) + scms, err := syscall.ParseSocketControlMessage(t.rdr.oob) if err != nil { return nil, err } @@ -148,11 +154,23 @@ func (t *unixTransport) ReadMessage() (*Message, error) { // 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 { - if j, ok := v.(UnixFDIndex); ok { + 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 @@ -175,7 +193,7 @@ func (t *unixTransport) SendMessage(msg *Message) error { msg.Headers[FieldUnixFDs] = MakeVariant(uint32(len(fds))) oob := syscall.UnixRights(fds...) buf := new(bytes.Buffer) - msg.EncodeTo(buf, binary.LittleEndian) + msg.EncodeTo(buf, nativeEndian) n, oobn, err := t.UnixConn.WriteMsgUnix(buf.Bytes(), oob, nil) if err != nil { return err @@ -184,7 +202,7 @@ func (t *unixTransport) SendMessage(msg *Message) error { return io.ErrShortWrite } } else { - if err := msg.EncodeTo(t, binary.LittleEndian); err != nil { + if err := msg.EncodeTo(t, nativeEndian); err != nil { return nil } } diff --git a/vendor/github.com/godbus/dbus/transport_unixcred_freebsd.go b/vendor/github.com/godbus/dbus/transport_unixcred_freebsd.go new file mode 100644 index 00000000..0fc5b927 --- /dev/null +++ b/vendor/github.com/godbus/dbus/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 +*/ +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/transport_unixcred_openbsd.go b/vendor/github.com/godbus/dbus/transport_unixcred_openbsd.go new file mode 100644 index 00000000..af7bafdf --- /dev/null +++ b/vendor/github.com/godbus/dbus/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/variant.go b/vendor/github.com/godbus/dbus/variant.go index b7b13ae9..0ca123b0 100644 --- a/vendor/github.com/godbus/dbus/variant.go +++ b/vendor/github.com/godbus/dbus/variant.go @@ -17,7 +17,12 @@ type Variant struct { // 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 Variant{SignatureOf(v), v} + 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 diff --git a/vendor/github.com/golang/protobuf/README.md b/vendor/github.com/golang/protobuf/README.md index 037fc7c8..9c4c815c 100644 --- a/vendor/github.com/golang/protobuf/README.md +++ b/vendor/github.com/golang/protobuf/README.md @@ -1,5 +1,8 @@ # Go support for Protocol Buffers +[![Build Status](https://travis-ci.org/golang/protobuf.svg?branch=master)](https://travis-ci.org/golang/protobuf) +[![GoDoc](https://godoc.org/github.com/golang/protobuf?status.svg)](https://godoc.org/github.com/golang/protobuf) + Google's data interchange format. Copyright 2010 The Go Authors. https://github.com/golang/protobuf @@ -22,7 +25,7 @@ To use this software, you must: for details or, if you are using gccgo, follow the instructions at https://golang.org/doc/install/gccgo - Grab the code from the repository and install the proto package. - The simplest way is to run `go get -u github.com/golang/protobuf/{proto,protoc-gen-go}`. + The simplest way is to run `go get -u github.com/golang/protobuf/protoc-gen-go`. The compiler plugin, protoc-gen-go, will be installed in $GOBIN, defaulting to $GOPATH/bin. It must be in your $PATH for the protocol compiler, protoc, to find it. @@ -104,12 +107,12 @@ for a protocol buffer variable v: When the .proto file specifies `syntax="proto3"`, there are some differences: - Non-repeated fields of non-message type are values instead of pointers. - - Getters are only generated for message and oneof fields. - Enum types do not get an Enum method. Consider file test.proto, containing ```proto + syntax = "proto2"; package example; enum FOO { X = 17; }; diff --git a/vendor/github.com/golang/protobuf/proto/discard.go b/vendor/github.com/golang/protobuf/proto/discard.go new file mode 100644 index 00000000..bd0e3bb4 --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/discard.go @@ -0,0 +1,151 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2017 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +package proto + +import ( + "fmt" + "reflect" + "strings" +) + +// DiscardUnknown recursively discards all unknown fields from this message +// and all embedded messages. +// +// When unmarshaling a message with unrecognized fields, the tags and values +// of such fields are preserved in the Message. This allows a later call to +// marshal to be able to produce a message that continues to have those +// unrecognized fields. To avoid this, DiscardUnknown is used to +// explicitly clear the unknown fields after unmarshaling. +// +// For proto2 messages, the unknown fields of message extensions are only +// discarded from messages that have been accessed via GetExtension. +func DiscardUnknown(m Message) { + discardLegacy(m) +} + +func discardLegacy(m Message) { + v := reflect.ValueOf(m) + if v.Kind() != reflect.Ptr || v.IsNil() { + return + } + v = v.Elem() + if v.Kind() != reflect.Struct { + return + } + t := v.Type() + + for i := 0; i < v.NumField(); i++ { + f := t.Field(i) + if strings.HasPrefix(f.Name, "XXX_") { + continue + } + vf := v.Field(i) + tf := f.Type + + // Unwrap tf to get its most basic type. + var isPointer, isSlice bool + if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { + isSlice = true + tf = tf.Elem() + } + if tf.Kind() == reflect.Ptr { + isPointer = true + tf = tf.Elem() + } + if isPointer && isSlice && tf.Kind() != reflect.Struct { + panic(fmt.Sprintf("%T.%s cannot be a slice of pointers to primitive types", m, f.Name)) + } + + switch tf.Kind() { + case reflect.Struct: + switch { + case !isPointer: + panic(fmt.Sprintf("%T.%s cannot be a direct struct value", m, f.Name)) + case isSlice: // E.g., []*pb.T + for j := 0; j < vf.Len(); j++ { + discardLegacy(vf.Index(j).Interface().(Message)) + } + default: // E.g., *pb.T + discardLegacy(vf.Interface().(Message)) + } + case reflect.Map: + switch { + case isPointer || isSlice: + panic(fmt.Sprintf("%T.%s cannot be a pointer to a map or a slice of map values", m, f.Name)) + default: // E.g., map[K]V + tv := vf.Type().Elem() + if tv.Kind() == reflect.Ptr && tv.Implements(protoMessageType) { // Proto struct (e.g., *T) + for _, key := range vf.MapKeys() { + val := vf.MapIndex(key) + discardLegacy(val.Interface().(Message)) + } + } + } + case reflect.Interface: + // Must be oneof field. + switch { + case isPointer || isSlice: + panic(fmt.Sprintf("%T.%s cannot be a pointer to a interface or a slice of interface values", m, f.Name)) + default: // E.g., test_proto.isCommunique_Union interface + if !vf.IsNil() && f.Tag.Get("protobuf_oneof") != "" { + vf = vf.Elem() // E.g., *test_proto.Communique_Msg + if !vf.IsNil() { + vf = vf.Elem() // E.g., test_proto.Communique_Msg + vf = vf.Field(0) // E.g., Proto struct (e.g., *T) or primitive value + if vf.Kind() == reflect.Ptr { + discardLegacy(vf.Interface().(Message)) + } + } + } + } + } + } + + if vf := v.FieldByName("XXX_unrecognized"); vf.IsValid() { + if vf.Type() != reflect.TypeOf([]byte{}) { + panic("expected XXX_unrecognized to be of type []byte") + } + vf.Set(reflect.ValueOf([]byte(nil))) + } + + // For proto2 messages, only discard unknown fields in message extensions + // that have been accessed via GetExtension. + if em, ok := extendable(m); ok { + // Ignore lock since discardLegacy is not concurrency safe. + emm, _ := em.extensionsRead() + for _, mx := range emm { + if m, ok := mx.value.(Message); ok { + discardLegacy(m) + } + } + } +} diff --git a/vendor/github.com/golang/protobuf/proto/encode.go b/vendor/github.com/golang/protobuf/proto/encode.go index 2b30f846..8b84d1b2 100644 --- a/vendor/github.com/golang/protobuf/proto/encode.go +++ b/vendor/github.com/golang/protobuf/proto/encode.go @@ -174,11 +174,11 @@ func sizeFixed32(x uint64) int { // This is the format used for the sint64 protocol buffer type. func (p *Buffer) EncodeZigzag64(x uint64) error { // use signed number to get arithmetic right shift. - return p.EncodeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) + return p.EncodeVarint((x << 1) ^ uint64((int64(x) >> 63))) } func sizeZigzag64(x uint64) int { - return sizeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) + return sizeVarint((x << 1) ^ uint64((int64(x) >> 63))) } // EncodeZigzag32 writes a zigzag-encoded 32-bit integer diff --git a/vendor/github.com/golang/protobuf/proto/lib.go b/vendor/github.com/golang/protobuf/proto/lib.go index ac4ddbc0..1c225504 100644 --- a/vendor/github.com/golang/protobuf/proto/lib.go +++ b/vendor/github.com/golang/protobuf/proto/lib.go @@ -73,7 +73,6 @@ for a protocol buffer variable v: When the .proto file specifies `syntax="proto3"`, there are some differences: - Non-repeated fields of non-message type are values instead of pointers. - - Getters are only generated for message and oneof fields. - Enum types do not get an Enum method. The simplest way to describe this is to see an example. diff --git a/vendor/github.com/golang/protobuf/proto/text_parser.go b/vendor/github.com/golang/protobuf/proto/text_parser.go index 61f83c1e..5e14513f 100644 --- a/vendor/github.com/golang/protobuf/proto/text_parser.go +++ b/vendor/github.com/golang/protobuf/proto/text_parser.go @@ -865,7 +865,7 @@ func (p *textParser) readAny(v reflect.Value, props *Properties) error { return p.readStruct(fv, terminator) case reflect.Uint32: if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { - fv.SetUint(uint64(x)) + fv.SetUint(x) return nil } case reflect.Uint64: diff --git a/vendor/github.com/mrunalp/fileutils/README.md b/vendor/github.com/mrunalp/fileutils/README.md index d1569248..6cb4140e 100644 --- a/vendor/github.com/mrunalp/fileutils/README.md +++ b/vendor/github.com/mrunalp/fileutils/README.md @@ -2,4 +2,4 @@ Collection of utilities for file manipulation in golang -The library is based on docker pkg/archive but does copies instead of handling archive formats. +The library is based on docker pkg/archive pkg/idtools but does copies instead of handling archive formats. diff --git a/vendor/github.com/mrunalp/fileutils/fileutils.go b/vendor/github.com/mrunalp/fileutils/fileutils.go index b60cb909..5a9818a2 100644 --- a/vendor/github.com/mrunalp/fileutils/fileutils.go +++ b/vendor/github.com/mrunalp/fileutils/fileutils.go @@ -139,19 +139,16 @@ func CopyDirectory(source string, dest string) error { return nil } - // Copy the file. - if err := CopyFile(path, filepath.Join(dest, relPath)); err != nil { - return err - } - - return nil + return CopyFile(path, filepath.Join(dest, relPath)) }) } +// Gives a number indicating the device driver to be used to access the passed device func major(device uint64) uint64 { return (device >> 8) & 0xfff } +// Gives a number that serves as a flag to the device driver for the passed device func minor(device uint64) uint64 { return (device & 0xff) | ((device >> 12) & 0xfff00) } diff --git a/vendor/github.com/pkg/errors/README.md b/vendor/github.com/pkg/errors/README.md index 273db3c9..6483ba2a 100644 --- a/vendor/github.com/pkg/errors/README.md +++ b/vendor/github.com/pkg/errors/README.md @@ -1,4 +1,4 @@ -# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) +# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) Package errors provides simple error handling primitives. @@ -47,6 +47,6 @@ We welcome pull requests, bug fixes and issue reports. With that said, the bar f Before proposing a change, please discuss your change by raising an issue. -## Licence +## License BSD-2-Clause diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go index 842ee804..7421f326 100644 --- a/vendor/github.com/pkg/errors/errors.go +++ b/vendor/github.com/pkg/errors/errors.go @@ -6,7 +6,7 @@ // return err // } // -// which applied recursively up the call stack results in error reports +// which when applied recursively up the call stack results in error reports // without context or debugging information. The errors package allows // programmers to add context to the failure path in their code in a way // that does not destroy the original value of the error. @@ -15,16 +15,17 @@ // // The errors.Wrap function returns a new error that adds context to the // original error by recording a stack trace at the point Wrap is called, -// and the supplied message. For example +// together with the supplied message. For example // // _, err := ioutil.ReadAll(r) // if err != nil { // return errors.Wrap(err, "read failed") // } // -// If additional control is required the errors.WithStack and errors.WithMessage -// functions destructure errors.Wrap into its component operations of annotating -// an error with a stack trace and an a message, respectively. +// If additional control is required, the errors.WithStack and +// errors.WithMessage functions destructure errors.Wrap into its component +// operations: annotating an error with a stack trace and with a message, +// respectively. // // Retrieving the cause of an error // @@ -38,7 +39,7 @@ // } // // can be inspected by errors.Cause. errors.Cause will recursively retrieve -// the topmost error which does not implement causer, which is assumed to be +// the topmost error that does not implement causer, which is assumed to be // the original cause. For example: // // switch err := errors.Cause(err).(type) { @@ -48,16 +49,16 @@ // // unknown error // } // -// causer interface is not exported by this package, but is considered a part -// of stable public API. +// Although the causer interface is not exported by this package, it is +// considered a part of its stable public interface. // // Formatted printing of errors // // All error values returned from this package implement fmt.Formatter and can -// be formatted by the fmt package. The following verbs are supported +// be formatted by the fmt package. The following verbs are supported: // // %s print the error. If the error has a Cause it will be -// printed recursively +// printed recursively. // %v see %s // %+v extended format. Each Frame of the error's StackTrace will // be printed in detail. @@ -65,13 +66,13 @@ // Retrieving the stack trace of an error or wrapper // // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are -// invoked. This information can be retrieved with the following interface. +// invoked. This information can be retrieved with the following interface: // // type stackTracer interface { // StackTrace() errors.StackTrace // } // -// Where errors.StackTrace is defined as +// The returned errors.StackTrace type is defined as // // type StackTrace []Frame // @@ -85,8 +86,8 @@ // } // } // -// stackTracer interface is not exported by this package, but is considered a part -// of stable public API. +// Although the stackTracer interface is not exported by this package, it is +// considered a part of its stable public interface. // // See the documentation for Frame.Format for more details. package errors @@ -192,7 +193,7 @@ func Wrap(err error, message string) error { } // Wrapf returns an error annotating err with a stack trace -// at the point Wrapf is call, and the format specifier. +// at the point Wrapf is called, and the format specifier. // If err is nil, Wrapf returns nil. func Wrapf(err error, format string, args ...interface{}) error { if err == nil { @@ -220,6 +221,18 @@ func WithMessage(err error, message string) error { } } +// WithMessagef annotates err with the format specifier. +// If err is nil, WithMessagef returns nil. +func WithMessagef(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } +} + type withMessage struct { cause error msg string diff --git a/vendor/github.com/pkg/errors/stack.go b/vendor/github.com/pkg/errors/stack.go index 6b1f2891..2874a048 100644 --- a/vendor/github.com/pkg/errors/stack.go +++ b/vendor/github.com/pkg/errors/stack.go @@ -46,7 +46,8 @@ func (f Frame) line() int { // // Format accepts flags that alter the printing of some verbs, as follows: // -// %+s path of source file relative to the compile time GOPATH +// %+s function name and path of source file relative to the compile time +// GOPATH separated by \n\t (\n\t) // %+v equivalent to %+s:%d func (f Frame) Format(s fmt.State, verb rune) { switch verb { @@ -79,6 +80,14 @@ func (f Frame) Format(s fmt.State, verb rune) { // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). type StackTrace []Frame +// Format formats the stack of Frames according to the fmt.Formatter interface. +// +// %s lists source files for each Frame in the stack +// %v lists the source file and line number for each Frame in the stack +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+v Prints filename, function, and line number for each Frame in the stack. func (st StackTrace) Format(s fmt.State, verb rune) { switch verb { case 'v': @@ -136,43 +145,3 @@ func funcname(name string) string { i = strings.Index(name, ".") return name[i+1:] } - -func trimGOPATH(name, file string) string { - // Here we want to get the source file path relative to the compile time - // GOPATH. As of Go 1.6.x there is no direct way to know the compiled - // GOPATH at runtime, but we can infer the number of path segments in the - // GOPATH. We note that fn.Name() returns the function name qualified by - // the import path, which does not include the GOPATH. Thus we can trim - // segments from the beginning of the file path until the number of path - // separators remaining is one more than the number of path separators in - // the function name. For example, given: - // - // GOPATH /home/user - // file /home/user/src/pkg/sub/file.go - // fn.Name() pkg/sub.Type.Method - // - // We want to produce: - // - // pkg/sub/file.go - // - // From this we can easily see that fn.Name() has one less path separator - // than our desired output. We count separators from the end of the file - // path until it finds two more than in the function name and then move - // one character forward to preserve the initial path segment without a - // leading separator. - const sep = "/" - goal := strings.Count(name, sep) + 2 - i := len(file) - for n := 0; n < goal; n++ { - i = strings.LastIndex(file[:i], sep) - if i == -1 { - // not enough separators found, set i so that the slice expression - // below leaves file unmodified - i = -len(sep) - break - } - } - // get back to 0 or trim the leading separator - file = file[i+len(sep):] - return file -} diff --git a/vendor/github.com/syndtr/gocapability/capability/capability.go b/vendor/github.com/syndtr/gocapability/capability/capability.go index c07c5579..61a90775 100644 --- a/vendor/github.com/syndtr/gocapability/capability/capability.go +++ b/vendor/github.com/syndtr/gocapability/capability/capability.go @@ -60,13 +60,74 @@ type Capabilities interface { Apply(kind CapType) error } -// NewPid create new initialized Capabilities object for given pid when it -// is nonzero, or for the current pid if pid is 0 +// NewPid initializes a new Capabilities object for given pid when +// it is nonzero, or for the current process if pid is 0. +// +// Deprecated: Replace with NewPid2. For example, replace: +// +// c, err := NewPid(0) +// if err != nil { +// return err +// } +// +// with: +// +// c, err := NewPid2(0) +// if err != nil { +// return err +// } +// err = c.Load() +// if err != nil { +// return err +// } func NewPid(pid int) (Capabilities, error) { + c, err := newPid(pid) + if err != nil { + return c, err + } + err = c.Load() + return c, err +} + +// NewPid2 initializes a new Capabilities object for given pid when +// it is nonzero, or for the current process if pid is 0. This +// does not load the process's current capabilities; to do that you +// must call Load explicitly. +func NewPid2(pid int) (Capabilities, error) { return newPid(pid) } -// NewFile create new initialized Capabilities object for given named file. -func NewFile(name string) (Capabilities, error) { - return newFile(name) +// NewFile initializes a new Capabilities object for given file path. +// +// Deprecated: Replace with NewFile2. For example, replace: +// +// c, err := NewFile(path) +// if err != nil { +// return err +// } +// +// with: +// +// c, err := NewFile2(path) +// if err != nil { +// return err +// } +// err = c.Load() +// if err != nil { +// return err +// } +func NewFile(path string) (Capabilities, error) { + c, err := newFile(path) + if err != nil { + return c, err + } + err = c.Load() + return c, err +} + +// NewFile2 creates a new initialized Capabilities object for given +// file path. This does not load the process's current capabilities; +// to do that you must call Load explicitly. +func NewFile2(path string) (Capabilities, error) { + return newFile(path) } diff --git a/vendor/github.com/syndtr/gocapability/capability/capability_linux.go b/vendor/github.com/syndtr/gocapability/capability/capability_linux.go index 205e0f70..1567dc81 100644 --- a/vendor/github.com/syndtr/gocapability/capability/capability_linux.go +++ b/vendor/github.com/syndtr/gocapability/capability/capability_linux.go @@ -103,21 +103,17 @@ func newPid(pid int) (c Capabilities, err error) { case linuxCapVer1: p := new(capsV1) p.hdr.version = capVers - p.hdr.pid = pid + p.hdr.pid = int32(pid) c = p case linuxCapVer2, linuxCapVer3: p := new(capsV3) p.hdr.version = capVers - p.hdr.pid = pid + p.hdr.pid = int32(pid) c = p default: err = errUnknownVers return } - err = c.Load() - if err != nil { - c = nil - } return } @@ -492,10 +488,6 @@ func (c *capsV3) Apply(kind CapType) (err error) { func newFile(path string) (c Capabilities, err error) { c = &capsFile{path: path} - err = c.Load() - if err != nil { - c = nil - } return } diff --git a/vendor/github.com/syndtr/gocapability/capability/syscall_linux.go b/vendor/github.com/syndtr/gocapability/capability/syscall_linux.go index eb717008..3d2bf692 100644 --- a/vendor/github.com/syndtr/gocapability/capability/syscall_linux.go +++ b/vendor/github.com/syndtr/gocapability/capability/syscall_linux.go @@ -13,7 +13,7 @@ import ( type capHeader struct { version uint32 - pid int + pid int32 } type capData struct { diff --git a/vendor/github.com/urfave/cli/README.md b/vendor/github.com/urfave/cli/README.md index a1e537c7..2bbbd8ea 100644 --- a/vendor/github.com/urfave/cli/README.md +++ b/vendor/github.com/urfave/cli/README.md @@ -30,6 +30,7 @@ applications in an expressive way. * [Flags](#flags) + [Placeholder Values](#placeholder-values) + [Alternate Names](#alternate-names) + + [Ordering](#ordering) + [Values from the Environment](#values-from-the-environment) + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others) * [Subcommands](#subcommands) @@ -450,6 +451,76 @@ That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error. +#### Ordering + +Flags for the application and commands are shown in the order they are defined. +However, it's possible to sort them from outside this library by using `FlagsByName` +or `CommandsByName` with `sort`. + +For example this: + + +``` go +package main + +import ( + "os" + "sort" + + "github.com/urfave/cli" +) + +func main() { + app := cli.NewApp() + + app.Flags = []cli.Flag { + cli.StringFlag{ + Name: "lang, l", + Value: "english", + Usage: "Language for the greeting", + }, + cli.StringFlag{ + Name: "config, c", + Usage: "Load configuration from `FILE`", + }, + } + + app.Commands = []cli.Command{ + { + Name: "complete", + Aliases: []string{"c"}, + Usage: "complete a task on the list", + Action: func(c *cli.Context) error { + return nil + }, + }, + { + Name: "add", + Aliases: []string{"a"}, + Usage: "add a task to the list", + Action: func(c *cli.Context) error { + return nil + }, + }, + } + + sort.Sort(cli.FlagsByName(app.Flags)) + sort.Sort(cli.CommandsByName(app.Commands)) + + app.Run(os.Args) +} +``` + +Will result in help output like: + +``` +--config FILE, -c FILE Load configuration from FILE +--lang value, -l value Language for the greeting (default: "english") +``` + #### Values from the Environment You can also have the default value set from the environment via `EnvVar`. e.g. @@ -889,16 +960,13 @@ SUPPORT: support@awesometown.example.com cli.AppHelpTemplate = `NAME: {{.Name}} - {{.Usage}} USAGE: - {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command -[command options]{{end}} {{if -.ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} + {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} {{if len .Authors}} -AUTHOR(S): +AUTHOR: {{range .Authors}}{{ . }}{{end}} {{end}}{{if .Commands}} COMMANDS: -{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t" -}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}} +{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}} GLOBAL OPTIONS: {{range .VisibleFlags}}{{.}} {{end}}{{end}}{{if .Copyright }} diff --git a/vendor/github.com/urfave/cli/app.go b/vendor/github.com/urfave/cli/app.go index b9adf469..51fc45d8 100644 --- a/vendor/github.com/urfave/cli/app.go +++ b/vendor/github.com/urfave/cli/app.go @@ -6,9 +6,7 @@ import ( "io/ioutil" "os" "path/filepath" - "reflect" "sort" - "strings" "time" ) @@ -19,11 +17,8 @@ var ( contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you." - errNonFuncAction = NewExitError("ERROR invalid Action type. "+ - fmt.Sprintf("Must be a func of type `cli.ActionFunc`. %s", contactSysadmin)+ - fmt.Sprintf("See %s", appActionDeprecationURL), 2) - errInvalidActionSignature = NewExitError("ERROR invalid Action signature. "+ - fmt.Sprintf("Must be `cli.ActionFunc`. %s", contactSysadmin)+ + errInvalidActionType = NewExitError("ERROR invalid Action type. "+ + fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ fmt.Sprintf("See %s", appActionDeprecationURL), 2) ) @@ -42,6 +37,8 @@ type App struct { ArgsUsage string // Version of the program Version string + // Description of the program + Description string // List of commands to execute Commands []Command // List of flags to parse @@ -88,6 +85,12 @@ type App struct { ErrWriter io.Writer // Other custom info Metadata map[string]interface{} + // Carries a function which returns app specific info. + ExtraInfo func() map[string]string + // CustomAppHelpTemplate the text template for app help topic. + // cli.go uses text/template to render templates. You can + // render custom help text by setting this variable. + CustomAppHelpTemplate string didSetup bool } @@ -148,10 +151,6 @@ func (a *App) Setup() { } } - if a.EnableBashCompletion { - a.appendFlag(BashCompletionFlag) - } - if !a.HideVersion { a.appendFlag(VersionFlag) } @@ -165,6 +164,10 @@ func (a *App) Setup() { if a.Metadata == nil { a.Metadata = make(map[string]interface{}) } + + if a.Writer == nil { + a.Writer = os.Stdout + } } // Run is the entry point to the cli app. Parses the arguments slice and routes @@ -172,8 +175,20 @@ func (a *App) Setup() { func (a *App) Run(arguments []string) (err error) { a.Setup() + // handle the completion flag separately from the flagset since + // completion could be attempted after a flag, but before its value was put + // on the command line. this causes the flagset to interpret the completion + // flag name as the value of the flag before it which is undesirable + // note that we can only do this because the shell autocomplete function + // always appends the completion flag at the end of the command + shellComplete, arguments := checkShellCompleteFlag(a, arguments) + // parse flags - set := flagSet(a.Name, a.Flags) + set, err := flagSet(a.Name, a.Flags) + if err != nil { + return err + } + set.SetOutput(ioutil.Discard) err = set.Parse(arguments[1:]) nerr := normalizeFlags(a.Flags, set) @@ -183,6 +198,7 @@ func (a *App) Run(arguments []string) (err error) { ShowAppHelp(context) return nerr } + context.shellComplete = shellComplete if checkCompletions(context) { return nil @@ -194,7 +210,7 @@ func (a *App) Run(arguments []string) (err error) { HandleExitCoder(err) return err } - fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.") + fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) ShowAppHelp(context) return err } @@ -224,7 +240,6 @@ func (a *App) Run(arguments []string) (err error) { if a.Before != nil { beforeErr := a.Before(context) if beforeErr != nil { - fmt.Fprintf(a.Writer, "%v\n\n", beforeErr) ShowAppHelp(context) HandleExitCoder(beforeErr) err = beforeErr @@ -241,6 +256,10 @@ func (a *App) Run(arguments []string) (err error) { } } + if a.Action == nil { + a.Action = helpCommand.Action + } + // Run default Action err = HandleAction(a.Action, context) @@ -282,13 +301,12 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { } a.Commands = newCmds - // append flags - if a.EnableBashCompletion { - a.appendFlag(BashCompletionFlag) + // parse flags + set, err := flagSet(a.Name, a.Flags) + if err != nil { + return err } - // parse flags - set := flagSet(a.Name, a.Flags) set.SetOutput(ioutil.Discard) err = set.Parse(ctx.Args().Tail()) nerr := normalizeFlags(a.Flags, set) @@ -315,7 +333,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { HandleExitCoder(err) return err } - fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.") + fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) ShowSubcommandHelp(context) return err } @@ -456,47 +474,24 @@ type Author struct { func (a Author) String() string { e := "" if a.Email != "" { - e = "<" + a.Email + "> " + e = " <" + a.Email + ">" } - return fmt.Sprintf("%v %v", a.Name, e) + return fmt.Sprintf("%v%v", a.Name, e) } -// HandleAction uses ✧✧✧reflection✧✧✧ to figure out if the given Action is an -// ActionFunc, a func with the legacy signature for Action, or some other -// invalid thing. If it's an ActionFunc or a func with the legacy signature for -// Action, the func is run! +// HandleAction attempts to figure out which Action signature was used. If +// it's an ActionFunc or a func with the legacy signature for Action, the func +// is run! func HandleAction(action interface{}, context *Context) (err error) { - defer func() { - if r := recover(); r != nil { - // Try to detect a known reflection error from *this scope*, rather than - // swallowing all panics that may happen when calling an Action func. - s := fmt.Sprintf("%v", r) - if strings.HasPrefix(s, "reflect: ") && strings.Contains(s, "too many input arguments") { - err = NewExitError(fmt.Sprintf("ERROR unknown Action error: %v.", r), 2) - } else { - panic(r) - } - } - }() - - if reflect.TypeOf(action).Kind() != reflect.Func { - return errNonFuncAction - } - - vals := reflect.ValueOf(action).Call([]reflect.Value{reflect.ValueOf(context)}) - - if len(vals) == 0 { + if a, ok := action.(ActionFunc); ok { + return a(context) + } else if a, ok := action.(func(*Context) error); ok { + return a(context) + } else if a, ok := action.(func(*Context)); ok { // deprecated function signature + a(context) return nil + } else { + return errInvalidActionType } - - if len(vals) > 1 { - return errInvalidActionSignature - } - - if retErr, ok := vals[0].Interface().(error); vals[0].IsValid() && ok { - return retErr - } - - return err } diff --git a/vendor/github.com/urfave/cli/cli.go b/vendor/github.com/urfave/cli/cli.go index 74fd101f..90c07eb8 100644 --- a/vendor/github.com/urfave/cli/cli.go +++ b/vendor/github.com/urfave/cli/cli.go @@ -12,6 +12,7 @@ // app.Usage = "say a greeting" // app.Action = func(c *cli.Context) error { // println("Greetings") +// return nil // } // // app.Run(os.Args) diff --git a/vendor/github.com/urfave/cli/command.go b/vendor/github.com/urfave/cli/command.go index 96253b69..23de2944 100644 --- a/vendor/github.com/urfave/cli/command.go +++ b/vendor/github.com/urfave/cli/command.go @@ -59,6 +59,25 @@ type Command struct { // Full name of command for help, defaults to full command name, including parent commands. HelpName string commandNamePath []string + + // CustomHelpTemplate the text template for the command help topic. + // cli.go uses text/template to render templates. You can + // render custom help text by setting this variable. + CustomHelpTemplate string +} + +type CommandsByName []Command + +func (c CommandsByName) Len() int { + return len(c) +} + +func (c CommandsByName) Less(i, j int) bool { + return c[i].Name < c[j].Name +} + +func (c CommandsByName) Swap(i, j int) { + c[i], c[j] = c[j], c[i] } // FullName returns the full name of the command. @@ -87,11 +106,10 @@ func (c Command) Run(ctx *Context) (err error) { ) } - if ctx.App.EnableBashCompletion { - c.Flags = append(c.Flags, BashCompletionFlag) + set, err := flagSet(c.Name, c.Flags) + if err != nil { + return err } - - set := flagSet(c.Name, c.Flags) set.SetOutput(ioutil.Discard) if c.SkipFlagParsing { @@ -132,18 +150,6 @@ func (c Command) Run(ctx *Context) (err error) { err = set.Parse(ctx.Args().Tail()) } - if err != nil { - if c.OnUsageError != nil { - err := c.OnUsageError(ctx, err, false) - HandleExitCoder(err) - return err - } - fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.") - fmt.Fprintln(ctx.App.Writer) - ShowCommandHelp(ctx, c.Name) - return err - } - nerr := normalizeFlags(c.Flags, set) if nerr != nil { fmt.Fprintln(ctx.App.Writer, nerr) @@ -153,11 +159,23 @@ func (c Command) Run(ctx *Context) (err error) { } context := NewContext(ctx.App, set, ctx) - + context.Command = c if checkCommandCompletions(context, c.Name) { return nil } + if err != nil { + if c.OnUsageError != nil { + err := c.OnUsageError(context, err, false) + HandleExitCoder(err) + return err + } + fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) + fmt.Fprintln(context.App.Writer) + ShowCommandHelp(context, c.Name) + return err + } + if checkCommandHelp(context, c.Name) { return nil } @@ -179,15 +197,16 @@ func (c Command) Run(ctx *Context) (err error) { if c.Before != nil { err = c.Before(context) if err != nil { - fmt.Fprintln(ctx.App.Writer, err) - fmt.Fprintln(ctx.App.Writer) - ShowCommandHelp(ctx, c.Name) + ShowCommandHelp(context, c.Name) HandleExitCoder(err) return err } } - context.Command = c + if c.Action == nil { + c.Action = helpSubcommand.Action + } + err = HandleAction(c.Action, context) if err != nil { @@ -228,14 +247,13 @@ func (c Command) startApp(ctx *Context) error { app.HelpName = app.Name } - if c.Description != "" { - app.Usage = c.Description - } else { - app.Usage = c.Usage - } + app.Usage = c.Usage + app.Description = c.Description + app.ArgsUsage = c.ArgsUsage // set CommandNotFound app.CommandNotFound = ctx.App.CommandNotFound + app.CustomAppHelpTemplate = c.CustomHelpTemplate // set the flags and commands app.Commands = c.Subcommands @@ -248,6 +266,7 @@ func (c Command) startApp(ctx *Context) error { app.Author = ctx.App.Author app.Email = ctx.App.Email app.Writer = ctx.App.Writer + app.ErrWriter = ctx.App.ErrWriter app.categories = CommandCategories{} for _, command := range c.Subcommands { @@ -270,6 +289,7 @@ func (c Command) startApp(ctx *Context) error { } else { app.Action = helpSubcommand.Action } + app.OnUsageError = c.OnUsageError for index, cc := range app.Commands { app.Commands[index].commandNamePath = []string{c.Name, cc.Name} diff --git a/vendor/github.com/urfave/cli/context.go b/vendor/github.com/urfave/cli/context.go index 492a7422..db94191e 100644 --- a/vendor/github.com/urfave/cli/context.go +++ b/vendor/github.com/urfave/cli/context.go @@ -3,9 +3,9 @@ package cli import ( "errors" "flag" - "os" "reflect" "strings" + "syscall" ) // Context is a type that is passed through to @@ -15,6 +15,7 @@ import ( type Context struct { App *App Command Command + shellComplete bool flagSet *flag.FlagSet setFlags map[string]bool parentContext *Context @@ -22,7 +23,13 @@ type Context struct { // NewContext creates a new context. For use in when invoking an App or Command action. func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { - return &Context{App: app, flagSet: set, parentContext: parentCtx} + c := &Context{App: app, flagSet: set, parentContext: parentCtx} + + if parentCtx != nil { + c.shellComplete = parentCtx.shellComplete + } + + return c } // NumFlags returns the number of flags set @@ -32,11 +39,13 @@ func (c *Context) NumFlags() int { // Set sets a context flag to a value. func (c *Context) Set(name, value string) error { + c.setFlags = nil return c.flagSet.Set(name, value) } // GlobalSet sets a context flag to a value on the global flagset func (c *Context) GlobalSet(name, value string) error { + globalContext(c).setFlags = nil return globalContext(c).flagSet.Set(name, value) } @@ -91,7 +100,7 @@ func (c *Context) IsSet(name string) bool { eachName(envVarValue.String(), func(envVar string) { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { + if _, ok := syscall.Getenv(envVar); ok { c.setFlags[name] = true return } @@ -147,6 +156,11 @@ func (c *Context) Parent() *Context { return c.parentContext } +// value returns the value of the flag coressponding to `name` +func (c *Context) value(name string) interface{} { + return c.flagSet.Lookup(name).Value.(flag.Getter).Get() +} + // Args contains apps console arguments type Args []string diff --git a/vendor/github.com/urfave/cli/errors.go b/vendor/github.com/urfave/cli/errors.go index c7d8c2f9..562b2953 100644 --- a/vendor/github.com/urfave/cli/errors.go +++ b/vendor/github.com/urfave/cli/errors.go @@ -24,7 +24,7 @@ func NewMultiError(err ...error) MultiError { return MultiError{Errors: err} } -// Error implents the error interface. +// Error implements the error interface. func (m MultiError) Error() string { errs := make([]string, len(m.Errors)) for i, err := range m.Errors { @@ -34,6 +34,10 @@ func (m MultiError) Error() string { return strings.Join(errs, "\n") } +type ErrorFormatter interface { + Format(s fmt.State, verb rune) +} + // ExitCoder is the interface checked by `App` and `Command` for a custom exit // code type ExitCoder interface { @@ -44,11 +48,11 @@ type ExitCoder interface { // ExitError fulfills both the builtin `error` interface and `ExitCoder` type ExitError struct { exitCode int - message string + message interface{} } // NewExitError makes a new *ExitError -func NewExitError(message string, exitCode int) *ExitError { +func NewExitError(message interface{}, exitCode int) *ExitError { return &ExitError{ exitCode: exitCode, message: message, @@ -58,7 +62,7 @@ func NewExitError(message string, exitCode int) *ExitError { // Error returns the string message, fulfilling the interface required by // `error` func (ee *ExitError) Error() string { - return ee.message + return fmt.Sprintf("%v", ee.message) } // ExitCode returns the exit code, fulfilling the interface required by @@ -70,7 +74,7 @@ func (ee *ExitError) ExitCode() int { // HandleExitCoder checks if the error fulfills the ExitCoder interface, and if // so prints the error to stderr (if it is non-empty) and calls OsExiter with the // given exit code. If the given error is a MultiError, then this func is -// called on all members of the Errors slice. +// called on all members of the Errors slice and calls OsExiter with the last exit code. func HandleExitCoder(err error) { if err == nil { return @@ -78,21 +82,34 @@ func HandleExitCoder(err error) { if exitErr, ok := err.(ExitCoder); ok { if err.Error() != "" { - fmt.Fprintln(ErrWriter, err) + if _, ok := exitErr.(ErrorFormatter); ok { + fmt.Fprintf(ErrWriter, "%+v\n", err) + } else { + fmt.Fprintln(ErrWriter, err) + } } OsExiter(exitErr.ExitCode()) return } if multiErr, ok := err.(MultiError); ok { - for _, merr := range multiErr.Errors { - HandleExitCoder(merr) - } + code := handleMultiError(multiErr) + OsExiter(code) return } - - if err.Error() != "" { - fmt.Fprintln(ErrWriter, err) - } - OsExiter(1) +} + +func handleMultiError(multiErr MultiError) int { + code := 1 + for _, merr := range multiErr.Errors { + if multiErr2, ok := merr.(MultiError); ok { + code = handleMultiError(multiErr2) + } else { + fmt.Fprintln(ErrWriter, merr) + if exitErr, ok := merr.(ExitCoder); ok { + code = exitErr.ExitCode() + } + } + } + return code } diff --git a/vendor/github.com/urfave/cli/flag.go b/vendor/github.com/urfave/cli/flag.go index e748c025..877ff352 100644 --- a/vendor/github.com/urfave/cli/flag.go +++ b/vendor/github.com/urfave/cli/flag.go @@ -3,24 +3,24 @@ package cli import ( "flag" "fmt" - "os" "reflect" "runtime" "strconv" "strings" + "syscall" "time" ) const defaultPlaceholder = "value" // BashCompletionFlag enables bash-completion for all commands and subcommands -var BashCompletionFlag = BoolFlag{ +var BashCompletionFlag Flag = BoolFlag{ Name: "generate-bash-completion", Hidden: true, } // VersionFlag prints the version for the application -var VersionFlag = BoolFlag{ +var VersionFlag Flag = BoolFlag{ Name: "version, v", Usage: "print the version", } @@ -28,7 +28,7 @@ var VersionFlag = BoolFlag{ // HelpFlag prints the help for all commands and subcommands // Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand // unless HideHelp is set to true) -var HelpFlag = BoolFlag{ +var HelpFlag Flag = BoolFlag{ Name: "help, h", Usage: "show help", } @@ -37,6 +37,21 @@ var HelpFlag = BoolFlag{ // to display a flag. var FlagStringer FlagStringFunc = stringifyFlag +// FlagsByName is a slice of Flag. +type FlagsByName []Flag + +func (f FlagsByName) Len() int { + return len(f) +} + +func (f FlagsByName) Less(i, j int) bool { + return f[i].GetName() < f[j].GetName() +} + +func (f FlagsByName) Swap(i, j int) { + f[i], f[j] = f[j], f[i] +} + // Flag is a common interface related to parsing flags in cli. // For more advanced flag parsing techniques, it is recommended that // this interface be implemented. @@ -47,13 +62,29 @@ type Flag interface { GetName() string } -func flagSet(name string, flags []Flag) *flag.FlagSet { +// errorableFlag is an interface that allows us to return errors during apply +// it allows flags defined in this library to return errors in a fashion backwards compatible +// TODO remove in v2 and modify the existing Flag interface to return errors +type errorableFlag interface { + Flag + + ApplyWithError(*flag.FlagSet) error +} + +func flagSet(name string, flags []Flag) (*flag.FlagSet, error) { set := flag.NewFlagSet(name, flag.ContinueOnError) for _, f := range flags { - f.Apply(set) + //TODO remove in v2 when errorableFlag is removed + if ef, ok := f.(errorableFlag); ok { + if err := ef.ApplyWithError(set); err != nil { + return nil, err + } + } else { + f.Apply(set) + } } - return set + return set, nil } func eachName(longName string, fn func(string)) { @@ -72,13 +103,22 @@ type Generic interface { // Apply takes the flagset and calls Set on the generic flag with the value // provided by the user for parsing by the flag +// Ignores parsing errors func (f GenericFlag) Apply(set *flag.FlagSet) { + f.ApplyWithError(set) +} + +// ApplyWithError takes the flagset and calls Set on the generic flag with the value +// provided by the user for parsing by the flag +func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error { val := f.Value if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { - val.Set(envVal) + if envVal, ok := syscall.Getenv(envVar); ok { + if err := val.Set(envVal); err != nil { + return fmt.Errorf("could not parse %s as value for flag %s: %s", envVal, f.Name, err) + } break } } @@ -87,9 +127,11 @@ func (f GenericFlag) Apply(set *flag.FlagSet) { eachName(f.Name, func(name string) { set.Var(f.Value, name, f.Usage) }) + + return nil } -// StringSlice is an opaque type for []string to satisfy flag.Value +// StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter type StringSlice []string // Set appends the string value to the list of values @@ -108,16 +150,29 @@ func (f *StringSlice) Value() []string { return *f } +// Get returns the slice of strings set by this flag +func (f *StringSlice) Get() interface{} { + return *f +} + // Apply populates the flag given the flag set and environment +// Ignores errors func (f StringSliceFlag) Apply(set *flag.FlagSet) { + f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { + if envVal, ok := syscall.Getenv(envVar); ok { newVal := &StringSlice{} for _, s := range strings.Split(envVal, ",") { s = strings.TrimSpace(s) - newVal.Set(s) + if err := newVal.Set(s); err != nil { + return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err) + } } f.Value = newVal break @@ -131,9 +186,11 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) { } set.Var(f.Value, name, f.Usage) }) + + return nil } -// IntSlice is an opaque type for []int to satisfy flag.Value +// IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter type IntSlice []int // Set parses the value into an integer and appends it to the list of values @@ -156,18 +213,28 @@ func (f *IntSlice) Value() []int { return *f } +// Get returns the slice of ints set by this flag +func (f *IntSlice) Get() interface{} { + return *f +} + // Apply populates the flag given the flag set and environment +// Ignores errors func (f IntSliceFlag) Apply(set *flag.FlagSet) { + f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { + if envVal, ok := syscall.Getenv(envVar); ok { newVal := &IntSlice{} for _, s := range strings.Split(envVal, ",") { s = strings.TrimSpace(s) - err := newVal.Set(s) - if err != nil { - fmt.Fprintf(ErrWriter, err.Error()) + if err := newVal.Set(s); err != nil { + return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err) } } f.Value = newVal @@ -182,9 +249,11 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) { } set.Var(f.Value, name, f.Usage) }) + + return nil } -// Int64Slice is an opaque type for []int to satisfy flag.Value +// Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter type Int64Slice []int64 // Set parses the value into an integer and appends it to the list of values @@ -207,18 +276,28 @@ func (f *Int64Slice) Value() []int64 { return *f } +// Get returns the slice of ints set by this flag +func (f *Int64Slice) Get() interface{} { + return *f +} + // Apply populates the flag given the flag set and environment +// Ignores errors func (f Int64SliceFlag) Apply(set *flag.FlagSet) { + f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { + if envVal, ok := syscall.Getenv(envVar); ok { newVal := &Int64Slice{} for _, s := range strings.Split(envVal, ",") { s = strings.TrimSpace(s) - err := newVal.Set(s) - if err != nil { - fmt.Fprintf(ErrWriter, err.Error()) + if err := newVal.Set(s); err != nil { + return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err) } } f.Value = newVal @@ -233,19 +312,33 @@ func (f Int64SliceFlag) Apply(set *flag.FlagSet) { } set.Var(f.Value, name, f.Usage) }) + return nil } // Apply populates the flag given the flag set and environment +// Ignores errors func (f BoolFlag) Apply(set *flag.FlagSet) { + f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { val := false if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { - envValBool, err := strconv.ParseBool(envVal) - if err == nil { - val = envValBool + if envVal, ok := syscall.Getenv(envVar); ok { + if envVal == "" { + val = false + break } + + envValBool, err := strconv.ParseBool(envVal) + if err != nil { + return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) + } + + val = envValBool break } } @@ -258,20 +351,35 @@ func (f BoolFlag) Apply(set *flag.FlagSet) { } set.Bool(name, val, f.Usage) }) + + return nil } // Apply populates the flag given the flag set and environment +// Ignores errors func (f BoolTFlag) Apply(set *flag.FlagSet) { + f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { val := true if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { - envValBool, err := strconv.ParseBool(envVal) - if err == nil { - val = envValBool + if envVal, ok := syscall.Getenv(envVar); ok { + if envVal == "" { + val = false break } + + envValBool, err := strconv.ParseBool(envVal) + if err != nil { + return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) + } + + val = envValBool + break } } } @@ -283,14 +391,22 @@ func (f BoolTFlag) Apply(set *flag.FlagSet) { } set.Bool(name, val, f.Usage) }) + + return nil } // Apply populates the flag given the flag set and environment +// Ignores errors func (f StringFlag) Apply(set *flag.FlagSet) { + f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f StringFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { + if envVal, ok := syscall.Getenv(envVar); ok { f.Value = envVal break } @@ -304,19 +420,28 @@ func (f StringFlag) Apply(set *flag.FlagSet) { } set.String(name, f.Value, f.Usage) }) + + return nil } // Apply populates the flag given the flag set and environment +// Ignores errors func (f IntFlag) Apply(set *flag.FlagSet) { + f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f IntFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { + if envVal, ok := syscall.Getenv(envVar); ok { envValInt, err := strconv.ParseInt(envVal, 0, 64) - if err == nil { - f.Value = int(envValInt) - break + if err != nil { + return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) } + f.Value = int(envValInt) + break } } } @@ -328,19 +453,29 @@ func (f IntFlag) Apply(set *flag.FlagSet) { } set.Int(name, f.Value, f.Usage) }) + + return nil } // Apply populates the flag given the flag set and environment +// Ignores errors func (f Int64Flag) Apply(set *flag.FlagSet) { + f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { + if envVal, ok := syscall.Getenv(envVar); ok { envValInt, err := strconv.ParseInt(envVal, 0, 64) - if err == nil { - f.Value = envValInt - break + if err != nil { + return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) } + + f.Value = envValInt + break } } } @@ -352,19 +487,29 @@ func (f Int64Flag) Apply(set *flag.FlagSet) { } set.Int64(name, f.Value, f.Usage) }) + + return nil } // Apply populates the flag given the flag set and environment +// Ignores errors func (f UintFlag) Apply(set *flag.FlagSet) { + f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { + if envVal, ok := syscall.Getenv(envVar); ok { envValInt, err := strconv.ParseUint(envVal, 0, 64) - if err == nil { - f.Value = uint(envValInt) - break + if err != nil { + return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err) } + + f.Value = uint(envValInt) + break } } } @@ -376,19 +521,29 @@ func (f UintFlag) Apply(set *flag.FlagSet) { } set.Uint(name, f.Value, f.Usage) }) + + return nil } // Apply populates the flag given the flag set and environment +// Ignores errors func (f Uint64Flag) Apply(set *flag.FlagSet) { + f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { + if envVal, ok := syscall.Getenv(envVar); ok { envValInt, err := strconv.ParseUint(envVal, 0, 64) - if err == nil { - f.Value = uint64(envValInt) - break + if err != nil { + return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) } + + f.Value = uint64(envValInt) + break } } } @@ -400,19 +555,29 @@ func (f Uint64Flag) Apply(set *flag.FlagSet) { } set.Uint64(name, f.Value, f.Usage) }) + + return nil } // Apply populates the flag given the flag set and environment +// Ignores errors func (f DurationFlag) Apply(set *flag.FlagSet) { + f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { + if envVal, ok := syscall.Getenv(envVar); ok { envValDuration, err := time.ParseDuration(envVal) - if err == nil { - f.Value = envValDuration - break + if err != nil { + return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err) } + + f.Value = envValDuration + break } } } @@ -424,18 +589,29 @@ func (f DurationFlag) Apply(set *flag.FlagSet) { } set.Duration(name, f.Value, f.Usage) }) + + return nil } // Apply populates the flag given the flag set and environment +// Ignores errors func (f Float64Flag) Apply(set *flag.FlagSet) { + f.ApplyWithError(set) +} + +// ApplyWithError populates the flag given the flag set and environment +func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { if f.EnvVar != "" { for _, envVar := range strings.Split(f.EnvVar, ",") { envVar = strings.TrimSpace(envVar) - if envVal := os.Getenv(envVar); envVal != "" { + if envVal, ok := syscall.Getenv(envVar); ok { envValFloat, err := strconv.ParseFloat(envVal, 10) - if err == nil { - f.Value = float64(envValFloat) + if err != nil { + return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err) } + + f.Value = float64(envValFloat) + break } } } @@ -447,12 +623,15 @@ func (f Float64Flag) Apply(set *flag.FlagSet) { } set.Float64(name, f.Value, f.Usage) }) + + return nil } func visibleFlags(fl []Flag) []Flag { visible := []Flag{} for _, flag := range fl { - if !flagValue(flag).FieldByName("Hidden").Bool() { + field := flagValue(flag).FieldByName("Hidden") + if !field.IsValid() || !field.Bool() { visible = append(visible, flag) } } @@ -545,9 +724,8 @@ func stringifyFlag(f Flag) string { needsPlaceholder := false defaultValueString := "" - val := fv.FieldByName("Value") - if val.IsValid() { + if val := fv.FieldByName("Value"); val.IsValid() { needsPlaceholder = true defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface()) diff --git a/vendor/github.com/urfave/cli/help.go b/vendor/github.com/urfave/cli/help.go index ba34719b..57ec98d5 100644 --- a/vendor/github.com/urfave/cli/help.go +++ b/vendor/github.com/urfave/cli/help.go @@ -13,27 +13,31 @@ import ( // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. var AppHelpTemplate = `NAME: - {{.Name}} - {{.Usage}} + {{.Name}}{{if .Usage}} - {{.Usage}}{{end}} USAGE: - {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} - {{if .Version}}{{if not .HideVersion}} + {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} + VERSION: - {{.Version}} - {{end}}{{end}}{{if len .Authors}} -AUTHOR(S): - {{range .Authors}}{{.}}{{end}} - {{end}}{{if .VisibleCommands}} + {{.Version}}{{end}}{{end}}{{if .Description}} + +DESCRIPTION: + {{.Description}}{{end}}{{if len .Authors}} + +AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: + {{range $index, $author := .Authors}}{{if $index}} + {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} + COMMANDS:{{range .VisibleCategories}}{{if .Name}} {{.Name}}:{{end}}{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} -{{end}}{{end}}{{if .VisibleFlags}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + GLOBAL OPTIONS: - {{range .VisibleFlags}}{{.}} - {{end}}{{end}}{{if .Copyright}} + {{range $index, $option := .VisibleFlags}}{{if $index}} + {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} + COPYRIGHT: - {{.Copyright}} - {{end}} + {{.Copyright}}{{end}} ` // CommandHelpTemplate is the text template for the command help topic. @@ -43,7 +47,7 @@ var CommandHelpTemplate = `NAME: {{.HelpName}} - {{.Usage}} USAGE: - {{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Category}} + {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} CATEGORY: {{.Category}}{{end}}{{if .Description}} @@ -60,10 +64,10 @@ OPTIONS: // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. var SubcommandHelpTemplate = `NAME: - {{.HelpName}} - {{.Usage}} + {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} USAGE: - {{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} + {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} COMMANDS:{{range .VisibleCategories}}{{if .Name}} {{.Name}}:{{end}}{{range .VisibleCommands}} @@ -108,17 +112,42 @@ var helpSubcommand = Command{ // Prints help for the App or Command type helpPrinter func(w io.Writer, templ string, data interface{}) +// Prints help for the App or Command with custom template function. +type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{}) + // HelpPrinter is a function that writes the help output. If not set a default // is used. The function signature is: // func(w io.Writer, templ string, data interface{}) var HelpPrinter helpPrinter = printHelp +// HelpPrinterCustom is same as HelpPrinter but +// takes a custom function for template function map. +var HelpPrinterCustom helpPrinterCustom = printHelpCustom + // VersionPrinter prints the version for the App var VersionPrinter = printVersion +// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code. +func ShowAppHelpAndExit(c *Context, exitCode int) { + ShowAppHelp(c) + os.Exit(exitCode) +} + // ShowAppHelp is an action that displays the help. -func ShowAppHelp(c *Context) error { - HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) +func ShowAppHelp(c *Context) (err error) { + if c.App.CustomAppHelpTemplate == "" { + HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) + return + } + customAppData := func() map[string]interface{} { + if c.App.ExtraInfo == nil { + return nil + } + return map[string]interface{}{ + "ExtraInfo": c.App.ExtraInfo, + } + } + HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData()) return nil } @@ -134,6 +163,12 @@ func DefaultAppComplete(c *Context) { } } +// ShowCommandHelpAndExit - exits with code after showing help +func ShowCommandHelpAndExit(c *Context, command string, code int) { + ShowCommandHelp(c, command) + os.Exit(code) +} + // ShowCommandHelp prints help for the given command func ShowCommandHelp(ctx *Context, command string) error { // show the subcommand help for a command with subcommands @@ -144,7 +179,11 @@ func ShowCommandHelp(ctx *Context, command string) error { for _, c := range ctx.App.Commands { if c.HasName(command) { - HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) + if c.CustomHelpTemplate != "" { + HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil) + } else { + HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) + } return nil } } @@ -187,10 +226,15 @@ func ShowCommandCompletions(ctx *Context, command string) { } } -func printHelp(out io.Writer, templ string, data interface{}) { +func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) { funcMap := template.FuncMap{ "join": strings.Join, } + if customFunc != nil { + for key, value := range customFunc { + funcMap[key] = value + } + } w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) @@ -206,10 +250,14 @@ func printHelp(out io.Writer, templ string, data interface{}) { w.Flush() } +func printHelp(out io.Writer, templ string, data interface{}) { + printHelpCustom(out, templ, data, nil) +} + func checkVersion(c *Context) bool { found := false - if VersionFlag.Name != "" { - eachName(VersionFlag.Name, func(name string) { + if VersionFlag.GetName() != "" { + eachName(VersionFlag.GetName(), func(name string) { if c.GlobalBool(name) || c.Bool(name) { found = true } @@ -220,8 +268,8 @@ func checkVersion(c *Context) bool { func checkHelp(c *Context) bool { found := false - if HelpFlag.Name != "" { - eachName(HelpFlag.Name, func(name string) { + if HelpFlag.GetName() != "" { + eachName(HelpFlag.GetName(), func(name string) { if c.GlobalBool(name) || c.Bool(name) { found = true } @@ -248,20 +296,43 @@ func checkSubcommandHelp(c *Context) bool { return false } -func checkCompletions(c *Context) bool { - if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion { - ShowCompletions(c) - return true +func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { + if !a.EnableBashCompletion { + return false, arguments } - return false + pos := len(arguments) - 1 + lastArg := arguments[pos] + + if lastArg != "--"+BashCompletionFlag.GetName() { + return false, arguments + } + + return true, arguments[:pos] +} + +func checkCompletions(c *Context) bool { + if !c.shellComplete { + return false + } + + if args := c.Args(); args.Present() { + name := args.First() + if cmd := c.App.Command(name); cmd != nil { + // let the command handle the completion + return false + } + } + + ShowCompletions(c) + return true } func checkCommandCompletions(c *Context, name string) bool { - if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { - ShowCommandCompletions(c, name) - return true + if !c.shellComplete { + return false } - return false + ShowCommandCompletions(c, name) + return true }