summaryrefslogtreecommitdiff
path: root/vendor/github.com/coreos/go-systemd/v22
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/coreos/go-systemd/v22')
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/LICENSE191
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/NOTICE5
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/activation/files.go67
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/activation/listeners.go103
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/activation/packetconns.go38
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/dbus/dbus.go240
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/dbus/methods.go600
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/dbus/properties.go237
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/dbus/set.go47
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/dbus/subscription.go333
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/dbus/subscription_set.go57
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/internal/dlopen/dlopen.go82
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/journal/journal.go225
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/sdjournal/functions.go66
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/sdjournal/journal.go1120
-rw-r--r--vendor/github.com/coreos/go-systemd/v22/sdjournal/read.go272
16 files changed, 3683 insertions, 0 deletions
diff --git a/vendor/github.com/coreos/go-systemd/v22/LICENSE b/vendor/github.com/coreos/go-systemd/v22/LICENSE
new file mode 100644
index 000000000..37ec93a14
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/LICENSE
@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+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]
+
+ 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.
diff --git a/vendor/github.com/coreos/go-systemd/v22/NOTICE b/vendor/github.com/coreos/go-systemd/v22/NOTICE
new file mode 100644
index 000000000..23a0ada2f
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/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/v22/activation/files.go b/vendor/github.com/coreos/go-systemd/v22/activation/files.go
new file mode 100644
index 000000000..29dd18def
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/activation/files.go
@@ -0,0 +1,67 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 activation implements primitives for systemd socket activation.
+package activation
+
+import (
+ "os"
+ "strconv"
+ "strings"
+ "syscall"
+)
+
+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"))
+ if err != nil || pid != os.Getpid() {
+ return nil
+ }
+
+ nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS"))
+ if err != nil || nfds == 0 {
+ 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)
+ 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/v22/activation/listeners.go b/vendor/github.com/coreos/go-systemd/v22/activation/listeners.go
new file mode 100644
index 000000000..3dbe2b087
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/activation/listeners.go
@@ -0,0 +1,103 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 activation
+
+import (
+ "crypto/tls"
+ "net"
+)
+
+// Listeners returns a slice containing a net.Listener for each matching socket type
+// passed to this process.
+//
+// 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() ([]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
+}
+
+// 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(tlsConfig *tls.Config) ([]net.Listener, error) {
+ listeners, err := Listeners()
+
+ if listeners == nil || err != nil {
+ return nil, err
+ }
+
+ if tlsConfig != nil {
+ for i, l := range listeners {
+ // Activate TLS only for TCP sockets
+ if l.Addr().Network() == "tcp" {
+ listeners[i] = tls.NewListener(l, tlsConfig)
+ }
+ }
+ }
+
+ 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 {
+ 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/v22/activation/packetconns.go b/vendor/github.com/coreos/go-systemd/v22/activation/packetconns.go
new file mode 100644
index 000000000..a97206785
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/activation/packetconns.go
@@ -0,0 +1,38 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 activation
+
+import (
+ "net"
+)
+
+// PacketConns returns a slice containing a net.PacketConn for each matching socket type
+// passed to this process.
+//
+// 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() ([]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/v22/dbus/dbus.go b/vendor/github.com/coreos/go-systemd/v22/dbus/dbus.go
new file mode 100644
index 000000000..91584a166
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/dbus/dbus.go
@@ -0,0 +1,240 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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.
+
+// Integration with the systemd D-Bus API. See http://www.freedesktop.org/wiki/Software/systemd/dbus/
+package dbus
+
+import (
+ "encoding/hex"
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/godbus/dbus/v5"
+)
+
+const (
+ alpha = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`
+ num = `0123456789`
+ alphanum = alpha + num
+ signalBuffer = 100
+)
+
+// needsEscape checks whether a byte in a potential dbus ObjectPath needs to be escaped
+func needsEscape(i int, b byte) bool {
+ // Escape everything that is not a-z-A-Z-0-9
+ // Also escape 0-9 if it's the first character
+ return strings.IndexByte(alphanum, b) == -1 ||
+ (i == 0 && strings.IndexByte(num, b) != -1)
+}
+
+// PathBusEscape sanitizes a constituent string of a dbus ObjectPath using the
+// rules that systemd uses for serializing special characters.
+func PathBusEscape(path string) string {
+ // Special case the empty string
+ if len(path) == 0 {
+ return "_"
+ }
+ n := []byte{}
+ for i := 0; i < len(path); i++ {
+ c := path[i]
+ if needsEscape(i, c) {
+ e := fmt.Sprintf("_%x", c)
+ n = append(n, []byte(e)...)
+ } else {
+ n = append(n, c)
+ }
+ }
+ 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
+ sysconn *dbus.Conn
+ sysobj dbus.BusObject
+
+ // sigconn/sigobj are only used to receive dbus signals
+ sigconn *dbus.Conn
+ sigobj dbus.BusObject
+
+ jobListener struct {
+ jobs map[dbus.ObjectPath]chan<- string
+ sync.Mutex
+ }
+ 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.
+// Callers should call Close() when done with the connection.
+func New() (*Conn, error) {
+ conn, err := NewSystemConnection()
+ if err != nil && os.Geteuid() == 0 {
+ return NewSystemdConnection()
+ }
+ return conn, err
+}
+
+// NewSystemConnection establishes a connection to the system bus and authenticates.
+// Callers should call Close() when done with the connection
+func NewSystemConnection() (*Conn, error) {
+ return NewConnection(func() (*dbus.Conn, error) {
+ return dbusAuthHelloConnection(dbus.SystemBusPrivate)
+ })
+}
+
+// NewUserConnection establishes a connection to the session bus and
+// authenticates. This can be used to connect to systemd user instances.
+// Callers should call Close() when done with the connection.
+func NewUserConnection() (*Conn, error) {
+ return NewConnection(func() (*dbus.Conn, error) {
+ return dbusAuthHelloConnection(dbus.SessionBusPrivate)
+ })
+}
+
+// NewSystemdConnection establishes a private, direct connection to systemd.
+// This can be used for communicating with systemd without a dbus daemon.
+// Callers should call Close() when done with the connection.
+func NewSystemdConnection() (*Conn, error) {
+ return NewConnection(func() (*dbus.Conn, error) {
+ // We skip Hello when talking directly to systemd.
+ return dbusAuthConnection(func(opts ...dbus.ConnOption) (*dbus.Conn, error) {
+ return dbus.Dial("unix:path=/run/systemd/private")
+ })
+ })
+}
+
+// Close closes an established connection
+func (c *Conn) Close() {
+ c.sysconn.Close()
+ c.sigconn.Close()
+}
+
+// NewConnection establishes a connection to a bus using a caller-supplied function.
+// This allows connecting to remote buses through a user-supplied mechanism.
+// The supplied function may be called multiple times, and should return independent connections.
+// The returned connection must be fully initialised: the org.freedesktop.DBus.Hello call must have succeeded,
+// and any authentication should be handled by the function.
+func NewConnection(dialBus func() (*dbus.Conn, error)) (*Conn, error) {
+ sysconn, err := dialBus()
+ if err != nil {
+ return nil, err
+ }
+
+ sigconn, err := dialBus()
+ if err != nil {
+ sysconn.Close()
+ return nil, err
+ }
+
+ c := &Conn{
+ sysconn: sysconn,
+ sysobj: systemdObject(sysconn),
+ sigconn: sigconn,
+ sigobj: systemdObject(sigconn),
+ }
+
+ 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
+ c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
+ "type='signal', interface='org.freedesktop.systemd1.Manager', member='JobRemoved'")
+
+ c.dispatch()
+ return c, nil
+}
+
+// GetManagerProperty returns the value of a property on the org.freedesktop.systemd1.Manager
+// interface. The value is returned in its string representation, as defined at
+// https://developer.gnome.org/glib/unstable/gvariant-text.html
+func (c *Conn) GetManagerProperty(prop string) (string, error) {
+ variant, err := c.sysobj.GetProperty("org.freedesktop.systemd1.Manager." + prop)
+ if err != nil {
+ return "", err
+ }
+ return variant.String(), nil
+}
+
+func dbusAuthConnection(createBus func(opts ...dbus.ConnOption) (*dbus.Conn, error)) (*dbus.Conn, error) {
+ conn, err := createBus()
+ if err != nil {
+ return nil, err
+ }
+
+ // Only use EXTERNAL method, and hardcode the uid (not username)
+ // to avoid a username lookup (which requires a dynamically linked
+ // libc)
+ methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))}
+
+ err = conn.Auth(methods)
+ if err != nil {
+ conn.Close()
+ return nil, err
+ }
+
+ return conn, nil
+}
+
+func dbusAuthHelloConnection(createBus func(opts ...dbus.ConnOption) (*dbus.Conn, error)) (*dbus.Conn, error) {
+ conn, err := dbusAuthConnection(createBus)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = conn.Hello(); err != nil {
+ conn.Close()
+ return nil, err
+ }
+
+ return conn, nil
+}
+
+func systemdObject(conn *dbus.Conn) dbus.BusObject {
+ return conn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
+}
diff --git a/vendor/github.com/coreos/go-systemd/v22/dbus/methods.go b/vendor/github.com/coreos/go-systemd/v22/dbus/methods.go
new file mode 100644
index 000000000..e38659d7b
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/dbus/methods.go
@@ -0,0 +1,600 @@
+// 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.
+// 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 dbus
+
+import (
+ "errors"
+ "fmt"
+ "path"
+ "strconv"
+
+ "github.com/godbus/dbus/v5"
+)
+
+func (c *Conn) jobComplete(signal *dbus.Signal) {
+ var id uint32
+ var job dbus.ObjectPath
+ var unit string
+ var result string
+ dbus.Store(signal.Body, &id, &job, &unit, &result)
+ c.jobListener.Lock()
+ out, ok := c.jobListener.jobs[job]
+ if ok {
+ out <- result
+ delete(c.jobListener.jobs, job)
+ }
+ c.jobListener.Unlock()
+}
+
+func (c *Conn) startJob(ch chan<- string, job string, args ...interface{}) (int, error) {
+ if ch != nil {
+ c.jobListener.Lock()
+ defer c.jobListener.Unlock()
+ }
+
+ var p dbus.ObjectPath
+ err := c.sysobj.Call(job, 0, args...).Store(&p)
+ if err != nil {
+ return 0, err
+ }
+
+ if ch != nil {
+ c.jobListener.jobs[p] = ch
+ }
+
+ // ignore error since 0 is fine if conversion fails
+ jobID, _ := strconv.Atoi(path.Base(string(p)))
+
+ return jobID, nil
+}
+
+// StartUnit enqueues a start job and depending jobs, if any (unless otherwise
+// specified by the mode string).
+//
+// Takes the unit to activate, plus a mode string. The mode needs to be one of
+// replace, fail, isolate, ignore-dependencies, ignore-requirements. If
+// "replace" the call will start the unit and its dependencies, possibly
+// replacing already queued jobs that conflict with this. If "fail" the call
+// will start the unit and its dependencies, but will fail if this would change
+// an already queued job. If "isolate" the call will start the unit in question
+// and terminate all units that aren't dependencies of it. If
+// "ignore-dependencies" it will start a unit but ignore all its dependencies.
+// If "ignore-requirements" it will start a unit but only ignore the
+// requirement dependencies. It is not recommended to make use of the latter
+// two options.
+//
+// If the provided channel is non-nil, a result string will be sent to it upon
+// job completion: one of done, canceled, timeout, failed, dependency, skipped.
+// done indicates successful execution of a job. canceled indicates that a job
+// has been canceled before it finished execution. timeout indicates that the
+// job timeout was reached. failed indicates that the job failed. dependency
+// indicates that a job this job has been depending on failed and the job hence
+// has been removed too. skipped indicates that a job was skipped because it
+// didn't apply to the units current state.
+//
+// If no error occurs, the ID of the underlying systemd job will be returned. There
+// does exist the possibility for no error to be returned, but for the returned job
+// ID to be 0. In this case, the actual underlying ID is not 0 and this datapoint
+// should not be considered authoritative.
+//
+// If an error does occur, it will be returned to the user alongside a job ID of 0.
+func (c *Conn) StartUnit(name string, mode string, ch chan<- string) (int, error) {
+ return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartUnit", name, mode)
+}
+
+// StopUnit is similar to StartUnit but stops the specified unit rather
+// than starting it.
+func (c *Conn) StopUnit(name string, mode string, ch chan<- string) (int, error) {
+ return c.startJob(ch, "org.freedesktop.systemd1.Manager.StopUnit", name, mode)
+}
+
+// ReloadUnit reloads a unit. Reloading is done only if the unit is already running and fails otherwise.
+func (c *Conn) ReloadUnit(name string, mode string, ch chan<- string) (int, error) {
+ return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadUnit", name, mode)
+}
+
+// RestartUnit restarts a service. If a service is restarted that isn't
+// running it will be started.
+func (c *Conn) RestartUnit(name string, mode string, ch chan<- string) (int, error) {
+ return c.startJob(ch, "org.freedesktop.systemd1.Manager.RestartUnit", name, mode)
+}
+
+// TryRestartUnit is like RestartUnit, except that a service that isn't running
+// is not affected by the restart.
+func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
+ return c.startJob(ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode)
+}
+
+// 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)
+}
+
+// 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)
+}
+
+// StartTransientUnit() may be used to create and start a transient unit, which
+// will be released as soon as it is not running or referenced anymore or the
+// system is rebooted. name is the unit name including suffix, and must be
+// unique. mode is the same as in StartUnit(), properties contains properties
+// of the unit.
+func (c *Conn) StartTransientUnit(name string, mode string, properties []Property, ch chan<- string) (int, error) {
+ return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0))
+}
+
+// KillUnit takes the unit name and a UNIX signal number to send. All of the unit's
+// processes are killed.
+func (c *Conn) KillUnit(name string, signal int32) {
+ c.sysobj.Call("org.freedesktop.systemd1.Manager.KillUnit", 0, name, "all", signal).Store()
+}
+
+// ResetFailedUnit resets the "failed" state of a specific unit.
+func (c *Conn) ResetFailedUnit(name string) error {
+ return c.sysobj.Call("org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store()
+}
+
+// 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
+
+ if !path.IsValid() {
+ return nil, fmt.Errorf("invalid unit name: %v", path)
+ }
+
+ obj := c.sysconn.Object("org.freedesktop.systemd1", path)
+ err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props)
+ if err != nil {
+ return nil, err
+ }
+
+ out := make(map[string]interface{}, len(props))
+ for k, v := range props {
+ out[k] = v.Value()
+ }
+
+ return out, nil
+}
+
+// GetUnitProperties takes the (unescaped) unit name and returns all of its dbus object properties.
+func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
+ 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")
+}
+
+// GetAllProperties takes the (unescaped) unit name and returns all of its dbus object properties.
+func (c *Conn) GetAllProperties(unit string) (map[string]interface{}, error) {
+ path := unitPath(unit)
+ return c.getProperties(path, "")
+}
+
+func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) {
+ var err error
+ var prop dbus.Variant
+
+ path := unitPath(unit)
+ if !path.IsValid() {
+ return nil, errors.New("invalid unit name: " + unit)
+ }
+
+ obj := c.sysconn.Object("org.freedesktop.systemd1", path)
+ err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, dbusInterface, propertyName).Store(&prop)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Property{Name: propertyName, Value: prop}, nil
+}
+
+func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) {
+ return c.getProperty(unit, "org.freedesktop.systemd1.Unit", propertyName)
+}
+
+// GetServiceProperty returns property for given service name and property name
+func (c *Conn) GetServiceProperty(service string, propertyName string) (*Property, error) {
+ return c.getProperty(service, "org.freedesktop.systemd1.Service", propertyName)
+}
+
+// GetUnitTypeProperties returns the extra properties for a unit, specific to the unit type.
+// 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) {
+ path := unitPath(unit)
+ return c.getProperties(path, "org.freedesktop.systemd1."+unitType)
+}
+
+// SetUnitProperties() may be used to modify certain unit properties at runtime.
+// Not all properties may be changed at runtime, but many resource management
+// settings (primarily those in systemd.cgroup(5)) may. The changes are applied
+// instantly, and stored on disk for future boots, unless runtime is true, in which
+// case the settings only apply until the next reboot. name is the name of the unit
+// to modify. properties are the settings to set, encoded as an array of property
+// name and value pairs.
+func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error {
+ return c.sysobj.Call("org.freedesktop.systemd1.Manager.SetUnitProperties", 0, name, runtime, properties).Store()
+}
+
+func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) {
+ return c.getProperty(unit, "org.freedesktop.systemd1."+unitType, propertyName)
+}
+
+type UnitStatus struct {
+ Name string // The primary unit name as string
+ Description string // The human readable description string
+ LoadState string // The load state (i.e. whether the unit file has been loaded successfully)
+ ActiveState string // The active state (i.e. whether the unit is currently started or not)
+ SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not)
+ Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string.
+ Path dbus.ObjectPath // The unit object path
+ JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise
+ JobType string // The job type as string
+ JobPath dbus.ObjectPath // The job object path
+}
+
+type storeFunc func(retvalues ...interface{}) error
+
+func (c *Conn) listUnitsInternal(f storeFunc) ([]UnitStatus, error) {
+ result := make([][]interface{}, 0)
+ err := f(&result)
+ if err != nil {
+ return nil, err
+ }
+
+ resultInterface := make([]interface{}, len(result))
+ for i := range result {
+ resultInterface[i] = result[i]
+ }
+
+ status := make([]UnitStatus, len(result))
+ statusInterface := make([]interface{}, len(status))
+ for i := range status {
+ statusInterface[i] = &status[i]
+ }
+
+ err = dbus.Store(resultInterface, statusInterface...)
+ if err != nil {
+ return nil, err
+ }
+
+ return status, nil
+}
+
+// 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)
+}
+
+// ListUnitsFiltered returns an array with units filtered by state.
+// It takes a list of units' statuses to filter.
+func (c *Conn) ListUnitsFiltered(states []string) ([]UnitStatus, error) {
+ return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsFiltered", 0, states).Store)
+}
+
+// ListUnitsByPatterns returns an array with units.
+// It takes a list of units' statuses and names to filter.
+// 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.
+func (c *Conn) ListUnitsByPatterns(states []string, patterns []string) ([]UnitStatus, error) {
+ return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsByPatterns", 0, states, patterns).Store)
+}
+
+// ListUnitsByNames returns an array with units. It takes a list of units'
+// 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)
+}
+
+type UnitFile struct {
+ Path string
+ Type string
+}
+
+func (c *Conn) listUnitFilesInternal(f storeFunc) ([]UnitFile, error) {
+ result := make([][]interface{}, 0)
+ err := f(&result)
+ if err != nil {
+ return nil, err
+ }
+
+ resultInterface := make([]interface{}, len(result))
+ for i := range result {
+ resultInterface[i] = result[i]
+ }
+
+ files := make([]UnitFile, len(result))
+ fileInterface := make([]interface{}, len(files))
+ for i := range files {
+ fileInterface[i] = &files[i]
+ }
+
+ err = dbus.Store(resultInterface, fileInterface...)
+ if err != nil {
+ return nil, err
+ }
+
+ return files, nil
+}
+
+// ListUnitFiles returns an array of all available units on disk.
+func (c *Conn) ListUnitFiles() ([]UnitFile, error) {
+ return c.listUnitFilesInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitFiles", 0).Store)
+}
+
+// ListUnitFilesByPatterns returns an array of all available units on disk matched the patterns.
+func (c *Conn) ListUnitFilesByPatterns(states []string, patterns []string) ([]UnitFile, error) {
+ return c.listUnitFilesInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitFilesByPatterns", 0, states, patterns).Store)
+}
+
+type LinkUnitFileChange EnableUnitFileChange
+
+// LinkUnitFiles() links unit files (that are located outside of the
+// usual unit search paths) into the unit search path.
+//
+// It takes a list of absolute paths to unit files to link and two
+// booleans. The first boolean controls whether the unit shall be
+// enabled for runtime only (true, /run), or persistently (false,
+// /etc).
+// The second controls whether symlinks pointing to other units shall
+// be replaced if necessary.
+//
+// This call returns a list of the changes made. The list consists of
+// structures with three strings: the type of the change (one of symlink
+// or unlink), the file name of the symlink and the destination of the
+// symlink.
+func (c *Conn) LinkUnitFiles(files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) {
+ result := make([][]interface{}, 0)
+ err := c.sysobj.Call("org.freedesktop.systemd1.Manager.LinkUnitFiles", 0, files, runtime, force).Store(&result)
+ if err != nil {
+ return nil, err
+ }
+
+ resultInterface := make([]interface{}, len(result))
+ for i := range result {
+ resultInterface[i] = result[i]
+ }
+
+ changes := make([]LinkUnitFileChange, len(result))
+ changesInterface := make([]interface{}, len(changes))
+ for i := range changes {
+ changesInterface[i] = &changes[i]
+ }
+
+ err = dbus.Store(resultInterface, changesInterface...)
+ if err != nil {
+ return nil, err
+ }
+
+ return changes, nil
+}
+
+// EnableUnitFiles() may be used to enable one or more units in the system (by
+// creating symlinks to them in /etc or /run).
+//
+// It takes a list of unit files to enable (either just file names or full
+// absolute paths if the unit files are residing outside the usual unit
+// search paths), and two booleans: the first controls whether the unit shall
+// be enabled for runtime only (true, /run), or persistently (false, /etc).
+// The second one controls whether symlinks pointing to other units shall
+// be replaced if necessary.
+//
+// This call returns one boolean and an array with the changes made. The
+// boolean signals whether the unit files contained any enablement
+// information (i.e. an [Install]) section. The changes list consists of
+// structures with three strings: the type of the change (one of symlink
+// or unlink), the file name of the symlink and the destination of the
+// symlink.
+func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) {
+ var carries_install_info bool
+
+ result := make([][]interface{}, 0)
+ err := c.sysobj.Call("org.freedesktop.systemd1.Manager.EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result)
+ if err != nil {
+ return false, nil, err
+ }
+
+ resultInterface := make([]interface{}, len(result))
+ for i := range result {
+ resultInterface[i] = result[i]
+ }
+
+ changes := make([]EnableUnitFileChange, len(result))
+ changesInterface := make([]interface{}, len(changes))
+ for i := range changes {
+ changesInterface[i] = &changes[i]
+ }
+
+ err = dbus.Store(resultInterface, changesInterface...)
+ if err != nil {
+ return false, nil, err
+ }
+
+ return carries_install_info, changes, nil
+}
+
+type EnableUnitFileChange struct {
+ Type string // Type of the change (one of symlink or unlink)
+ Filename string // File name of the symlink
+ Destination string // Destination of the symlink
+}
+
+// DisableUnitFiles() may be used to disable one or more units in the system (by
+// removing symlinks to them from /etc or /run).
+//
+// It takes a list of unit files to disable (either just file names or full
+// absolute paths if the unit files are residing outside the usual unit
+// search paths), and one boolean: whether the unit was enabled for runtime
+// only (true, /run), or persistently (false, /etc).
+//
+// This call returns an array with the changes made. The changes list
+// consists of structures with three strings: the type of the change (one of
+// symlink or unlink), the file name of the symlink and the destination of the
+// symlink.
+func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) {
+ result := make([][]interface{}, 0)
+ err := c.sysobj.Call("org.freedesktop.systemd1.Manager.DisableUnitFiles", 0, files, runtime).Store(&result)
+ if err != nil {
+ return nil, err
+ }
+
+ resultInterface := make([]interface{}, len(result))
+ for i := range result {
+ resultInterface[i] = result[i]
+ }
+
+ changes := make([]DisableUnitFileChange, len(result))
+ changesInterface := make([]interface{}, len(changes))
+ for i := range changes {
+ changesInterface[i] = &changes[i]
+ }
+
+ err = dbus.Store(resultInterface, changesInterface...)
+ if err != nil {
+ return nil, err
+ }
+
+ return changes, nil
+}
+
+type DisableUnitFileChange struct {
+ Type string // Type of the change (one of symlink or unlink)
+ Filename string // File name of the symlink
+ Destination string // Destination of the symlink
+}
+
+// MaskUnitFiles masks one or more units in the system
+//
+// It takes three arguments:
+// * list of units to mask (either just file names or full
+// absolute paths if the unit files are residing outside
+// the usual unit search paths)
+// * runtime to specify whether the unit was enabled for runtime
+// only (true, /run/systemd/..), or persistently (false, /etc/systemd/..)
+// * force flag
+func (c *Conn) MaskUnitFiles(files []string, runtime bool, force bool) ([]MaskUnitFileChange, error) {
+ result := make([][]interface{}, 0)
+ err := c.sysobj.Call("org.freedesktop.systemd1.Manager.MaskUnitFiles", 0, files, runtime, force).Store(&result)
+ if err != nil {
+ return nil, err
+ }
+
+ resultInterface := make([]interface{}, len(result))
+ for i := range result {
+ resultInterface[i] = result[i]
+ }
+
+ changes := make([]MaskUnitFileChange, len(result))
+ changesInterface := make([]interface{}, len(changes))
+ for i := range changes {
+ changesInterface[i] = &changes[i]
+ }
+
+ err = dbus.Store(resultInterface, changesInterface...)
+ if err != nil {
+ return nil, err
+ }
+
+ return changes, nil
+}
+
+type MaskUnitFileChange struct {
+ Type string // Type of the change (one of symlink or unlink)
+ Filename string // File name of the symlink
+ Destination string // Destination of the symlink
+}
+
+// UnmaskUnitFiles unmasks one or more units in the system
+//
+// It takes two arguments:
+// * list of unit files to mask (either just file names or full
+// absolute paths if the unit files are residing outside
+// the usual unit search paths)
+// * runtime to specify whether the unit was enabled for runtime
+// only (true, /run/systemd/..), or persistently (false, /etc/systemd/..)
+func (c *Conn) UnmaskUnitFiles(files []string, runtime bool) ([]UnmaskUnitFileChange, error) {
+ result := make([][]interface{}, 0)
+ err := c.sysobj.Call("org.freedesktop.systemd1.Manager.UnmaskUnitFiles", 0, files, runtime).Store(&result)
+ if err != nil {
+ return nil, err
+ }
+
+ resultInterface := make([]interface{}, len(result))
+ for i := range result {
+ resultInterface[i] = result[i]
+ }
+
+ changes := make([]UnmaskUnitFileChange, len(result))
+ changesInterface := make([]interface{}, len(changes))
+ for i := range changes {
+ changesInterface[i] = &changes[i]
+ }
+
+ err = dbus.Store(resultInterface, changesInterface...)
+ if err != nil {
+ return nil, err
+ }
+
+ return changes, nil
+}
+
+type UnmaskUnitFileChange struct {
+ Type string // Type of the change (one of symlink or unlink)
+ Filename string // File name of the symlink
+ Destination string // Destination of the symlink
+}
+
+// Reload instructs systemd to scan for and reload unit files. This is
+// equivalent to a 'systemctl daemon-reload'.
+func (c *Conn) Reload() error {
+ return c.sysobj.Call("org.freedesktop.systemd1.Manager.Reload", 0).Store()
+}
+
+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/v22/dbus/properties.go b/vendor/github.com/coreos/go-systemd/v22/dbus/properties.go
new file mode 100644
index 000000000..fb42b6273
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/dbus/properties.go
@@ -0,0 +1,237 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 dbus
+
+import (
+ "github.com/godbus/dbus/v5"
+)
+
+// From the systemd docs:
+//
+// The properties array of StartTransientUnit() may take many of the settings
+// that may also be configured in unit files. Not all parameters are currently
+// accepted though, but we plan to cover more properties with future release.
+// Currently you may set the Description, Slice and all dependency types of
+// units, as well as RemainAfterExit, ExecStart for service units,
+// TimeoutStopUSec and PIDs for scope units, and CPUAccounting, CPUShares,
+// BlockIOAccounting, BlockIOWeight, BlockIOReadBandwidth,
+// BlockIOWriteBandwidth, BlockIODeviceWeight, MemoryAccounting, MemoryLimit,
+// DevicePolicy, DeviceAllow for services/scopes/slices. These fields map
+// directly to their counterparts in unit files and as normal D-Bus object
+// properties. The exception here is the PIDs field of scope units which is
+// used for construction of the scope only and specifies the initial PIDs to
+// add to the scope object.
+
+type Property struct {
+ Name string
+ Value dbus.Variant
+}
+
+type PropertyCollection struct {
+ Name string
+ Properties []Property
+}
+
+type execStart struct {
+ Path string // the binary path to execute
+ Args []string // an array with all arguments to pass to the executed command, starting with argument 0
+ UncleanIsFailure bool // a boolean whether it should be considered a failure if the process exits uncleanly
+}
+
+// PropExecStart sets the ExecStart service property. The first argument is a
+// slice with the binary path to execute followed by the arguments to pass to
+// the executed command. See
+// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
+func PropExecStart(command []string, uncleanIsFailure bool) Property {
+ execStarts := []execStart{
+ {
+ Path: command[0],
+ Args: command,
+ UncleanIsFailure: uncleanIsFailure,
+ },
+ }
+
+ return Property{
+ Name: "ExecStart",
+ Value: dbus.MakeVariant(execStarts),
+ }
+}
+
+// PropRemainAfterExit sets the RemainAfterExit service property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit=
+func PropRemainAfterExit(b bool) Property {
+ return Property{
+ Name: "RemainAfterExit",
+ Value: dbus.MakeVariant(b),
+ }
+}
+
+// PropType sets the Type service property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=
+func PropType(t string) Property {
+ return Property{
+ Name: "Type",
+ Value: dbus.MakeVariant(t),
+ }
+}
+
+// PropDescription sets the Description unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description=
+func PropDescription(desc string) Property {
+ return Property{
+ Name: "Description",
+ Value: dbus.MakeVariant(desc),
+ }
+}
+
+func propDependency(name string, units []string) Property {
+ return Property{
+ Name: name,
+ Value: dbus.MakeVariant(units),
+ }
+}
+
+// PropRequires sets the Requires unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requires=
+func PropRequires(units ...string) Property {
+ return propDependency("Requires", units)
+}
+
+// PropRequiresOverridable sets the RequiresOverridable unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresOverridable=
+func PropRequiresOverridable(units ...string) Property {
+ return propDependency("RequiresOverridable", units)
+}
+
+// PropRequisite sets the Requisite unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requisite=
+func PropRequisite(units ...string) Property {
+ return propDependency("Requisite", units)
+}
+
+// PropRequisiteOverridable sets the RequisiteOverridable unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequisiteOverridable=
+func PropRequisiteOverridable(units ...string) Property {
+ return propDependency("RequisiteOverridable", units)
+}
+
+// PropWants sets the Wants unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Wants=
+func PropWants(units ...string) Property {
+ return propDependency("Wants", units)
+}
+
+// PropBindsTo sets the BindsTo unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo=
+func PropBindsTo(units ...string) Property {
+ return propDependency("BindsTo", units)
+}
+
+// PropRequiredBy sets the RequiredBy unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredBy=
+func PropRequiredBy(units ...string) Property {
+ return propDependency("RequiredBy", units)
+}
+
+// PropRequiredByOverridable sets the RequiredByOverridable unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredByOverridable=
+func PropRequiredByOverridable(units ...string) Property {
+ return propDependency("RequiredByOverridable", units)
+}
+
+// PropWantedBy sets the WantedBy unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy=
+func PropWantedBy(units ...string) Property {
+ return propDependency("WantedBy", units)
+}
+
+// PropBoundBy sets the BoundBy unit property. See
+// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#BoundBy=
+func PropBoundBy(units ...string) Property {
+ return propDependency("BoundBy", units)
+}
+
+// PropConflicts sets the Conflicts unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts=
+func PropConflicts(units ...string) Property {
+ return propDependency("Conflicts", units)
+}
+
+// PropConflictedBy sets the ConflictedBy unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConflictedBy=
+func PropConflictedBy(units ...string) Property {
+ return propDependency("ConflictedBy", units)
+}
+
+// PropBefore sets the Before unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before=
+func PropBefore(units ...string) Property {
+ return propDependency("Before", units)
+}
+
+// PropAfter sets the After unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#After=
+func PropAfter(units ...string) Property {
+ return propDependency("After", units)
+}
+
+// PropOnFailure sets the OnFailure unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#OnFailure=
+func PropOnFailure(units ...string) Property {
+ return propDependency("OnFailure", units)
+}
+
+// PropTriggers sets the Triggers unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Triggers=
+func PropTriggers(units ...string) Property {
+ return propDependency("Triggers", units)
+}
+
+// PropTriggeredBy sets the TriggeredBy unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#TriggeredBy=
+func PropTriggeredBy(units ...string) Property {
+ return propDependency("TriggeredBy", units)
+}
+
+// PropPropagatesReloadTo sets the PropagatesReloadTo unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#PropagatesReloadTo=
+func PropPropagatesReloadTo(units ...string) Property {
+ return propDependency("PropagatesReloadTo", units)
+}
+
+// PropRequiresMountsFor sets the RequiresMountsFor unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresMountsFor=
+func PropRequiresMountsFor(units ...string) Property {
+ return propDependency("RequiresMountsFor", units)
+}
+
+// PropSlice sets the Slice unit property. See
+// http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Slice=
+func PropSlice(slice string) Property {
+ return Property{
+ Name: "Slice",
+ Value: dbus.MakeVariant(slice),
+ }
+}
+
+// PropPids sets the PIDs field of scope units used in the initial construction
+// of the scope only and specifies the initial PIDs to add to the scope object.
+// See https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/#properties
+func PropPids(pids ...uint32) Property {
+ return Property{
+ Name: "PIDs",
+ Value: dbus.MakeVariant(pids),
+ }
+}
diff --git a/vendor/github.com/coreos/go-systemd/v22/dbus/set.go b/vendor/github.com/coreos/go-systemd/v22/dbus/set.go
new file mode 100644
index 000000000..17c5d4856
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/dbus/set.go
@@ -0,0 +1,47 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 dbus
+
+type set struct {
+ data map[string]bool
+}
+
+func (s *set) Add(value string) {
+ s.data[value] = true
+}
+
+func (s *set) Remove(value string) {
+ delete(s.data, value)
+}
+
+func (s *set) Contains(value string) (exists bool) {
+ _, exists = s.data[value]
+ return
+}
+
+func (s *set) Length() int {
+ return len(s.data)
+}
+
+func (s *set) Values() (values []string) {
+ for val := range s.data {
+ values = append(values, val)
+ }
+ return
+}
+
+func newSet() *set {
+ return &set{make(map[string]bool)}
+}
diff --git a/vendor/github.com/coreos/go-systemd/v22/dbus/subscription.go b/vendor/github.com/coreos/go-systemd/v22/dbus/subscription.go
new file mode 100644
index 000000000..7e370fea2
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/dbus/subscription.go
@@ -0,0 +1,333 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 dbus
+
+import (
+ "errors"
+ "log"
+ "time"
+
+ "github.com/godbus/dbus/v5"
+)
+
+const (
+ cleanIgnoreInterval = int64(10 * time.Second)
+ ignoreInterval = int64(30 * time.Millisecond)
+)
+
+// Subscribe sets up this connection to subscribe to all systemd dbus events.
+// This is required before calling SubscribeUnits. When the connection closes
+// systemd will automatically stop sending signals so there is no need to
+// explicitly call Unsubscribe().
+func (c *Conn) Subscribe() error {
+ c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
+ "type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'")
+ c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
+ "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'")
+
+ return c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
+}
+
+// Unsubscribe this connection from systemd dbus events.
+func (c *Conn) Unsubscribe() error {
+ return c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
+}
+
+func (c *Conn) dispatch() {
+ ch := make(chan *dbus.Signal, signalBuffer)
+
+ c.sigconn.Signal(ch)
+
+ go func() {
+ for {
+ signal, ok := <-ch
+ if !ok {
+ return
+ }
+
+ if signal.Name == "org.freedesktop.systemd1.Manager.JobRemoved" {
+ c.jobComplete(signal)
+ }
+
+ if c.subStateSubscriber.updateCh == nil &&
+ c.propertiesSubscriber.updateCh == nil {
+ continue
+ }
+
+ var unitPath dbus.ObjectPath
+ switch signal.Name {
+ case "org.freedesktop.systemd1.Manager.JobRemoved":
+ unitName := signal.Body[2].(string)
+ c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath)
+ case "org.freedesktop.systemd1.Manager.UnitNew":
+ unitPath = signal.Body[1].(dbus.ObjectPath)
+ 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)
+ }
+ }
+ }
+ }
+
+ if unitPath == dbus.ObjectPath("") {
+ continue
+ }
+
+ c.sendSubStateUpdate(unitPath)
+ }
+ }()
+}
+
+// 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)
+}
+
+// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer
+// size of the channels, the comparison function for detecting changes and a filter
+// function for cutting down on the noise that your channel receives.
+func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func(string) bool) (<-chan map[string]*UnitStatus, <-chan error) {
+ old := make(map[string]*UnitStatus)
+ statusChan := make(chan map[string]*UnitStatus, buffer)
+ errChan := make(chan error, buffer)
+
+ go func() {
+ for {
+ timerChan := time.After(interval)
+
+ units, err := c.ListUnits()
+ if err == nil {
+ cur := make(map[string]*UnitStatus)
+ for i := range units {
+ if filterUnit != nil && filterUnit(units[i].Name) {
+ continue
+ }
+ cur[units[i].Name] = &units[i]
+ }
+
+ // add all new or changed units
+ changed := make(map[string]*UnitStatus)
+ for n, u := range cur {
+ if oldU, ok := old[n]; !ok || isChanged(oldU, u) {
+ changed[n] = u
+ }
+ delete(old, n)
+ }
+
+ // add all deleted units
+ for oldN := range old {
+ changed[oldN] = nil
+ }
+
+ old = cur
+
+ if len(changed) != 0 {
+ statusChan <- changed
+ }
+ } else {
+ errChan <- err
+ }
+
+ <-timerChan
+ }
+ }()
+
+ return statusChan, errChan
+}
+
+type SubStateUpdate struct {
+ UnitName string
+ SubState string
+}
+
+// SetSubStateSubscriber writes to updateCh when any unit's substate changes.
+// Although this writes to updateCh on every state change, the reported state
+// may be more recent than the change that generated it (due to an unavoidable
+// race in the systemd dbus interface). That is, this method provides a good
+// way to keep a current view of all units' states, but is not guaranteed to
+// show every state transition they go through. Furthermore, 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) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) {
+ if c == nil {
+ msg := "nil receiver"
+ select {
+ case errCh <- errors.New(msg):
+ default:
+ log.Printf("full error channel while reporting: %s\n", msg)
+ }
+ return
+ }
+
+ 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
+ }
+
+ 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.subStateSubscriber.updateCh <- update:
+ default:
+ msg := "update channel is full"
+ select {
+ case c.subStateSubscriber.errCh <- errors.New(msg):
+ default:
+ log.Printf("full error channel while reporting: %s\n", msg)
+ }
+ return
+ }
+}
+
+// The ignore functions work around a wart in the systemd dbus interface.
+// Requesting the properties of an unloaded unit will cause systemd to send a
+// pair of UnitNew/UnitRemoved signals. Because we need to get a unit's
+// properties on UnitNew (as that's the only indication of a new unit coming up
+// for the first time), we would enter an infinite loop if we did not attempt
+// to detect and ignore these spurious signals. The signal themselves are
+// indistinguishable from relevant ones, so we (somewhat hackishly) ignore an
+// unloaded unit's signals for a short time after requesting its properties.
+// This means that we will miss e.g. a transient unit being restarted
+// *immediately* upon failure and also a transient unit being started
+// immediately after requesting its status (with systemctl status, for example,
+// because this causes a UnitNew signal to be sent which then causes us to fetch
+// the properties).
+
+func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool {
+ t, ok := c.subStateSubscriber.ignore[path]
+ return ok && t >= time.Now().UnixNano()
+}
+
+func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) {
+ loadState, ok := info["LoadState"].(string)
+ if !ok {
+ return
+ }
+
+ // unit is unloaded - it will trigger bad systemd dbus behavior
+ 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.subStateSubscriber.cleanIgnore < now {
+ c.subStateSubscriber.cleanIgnore = now + cleanIgnoreInterval
+
+ for p, t := range c.subStateSubscriber.ignore {
+ if t < now {
+ 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/coreos/go-systemd/v22/dbus/subscription_set.go b/vendor/github.com/coreos/go-systemd/v22/dbus/subscription_set.go
new file mode 100644
index 000000000..5b408d584
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/dbus/subscription_set.go
@@ -0,0 +1,57 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 dbus
+
+import (
+ "time"
+)
+
+// SubscriptionSet returns a subscription set which is like conn.Subscribe but
+// can filter to only return events for a set of units.
+type SubscriptionSet struct {
+ *set
+ conn *Conn
+}
+
+func (s *SubscriptionSet) filter(unit string) bool {
+ return !s.Contains(unit)
+}
+
+// Subscribe starts listening for dbus events for all of the units in the set.
+// Returns channels identical to conn.SubscribeUnits.
+func (s *SubscriptionSet) Subscribe() (<-chan map[string]*UnitStatus, <-chan error) {
+ // TODO: Make fully evented by using systemd 209 with properties changed values
+ return s.conn.SubscribeUnitsCustom(time.Second, 0,
+ mismatchUnitStatus,
+ func(unit string) bool { return s.filter(unit) },
+ )
+}
+
+// NewSubscriptionSet returns a new subscription set.
+func (conn *Conn) NewSubscriptionSet() *SubscriptionSet {
+ return &SubscriptionSet{newSet(), conn}
+}
+
+// mismatchUnitStatus returns true if the provided UnitStatus objects
+// are not equivalent. false is returned if the objects are equivalent.
+// Only the Name, Description and state-related fields are used in
+// the comparison.
+func mismatchUnitStatus(u1, u2 *UnitStatus) bool {
+ return u1.Name != u2.Name ||
+ u1.Description != u2.Description ||
+ u1.LoadState != u2.LoadState ||
+ u1.ActiveState != u2.ActiveState ||
+ u1.SubState != u2.SubState
+}
diff --git a/vendor/github.com/coreos/go-systemd/v22/internal/dlopen/dlopen.go b/vendor/github.com/coreos/go-systemd/v22/internal/dlopen/dlopen.go
new file mode 100644
index 000000000..23774f612
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/internal/dlopen/dlopen.go
@@ -0,0 +1,82 @@
+// Copyright 2016 CoreOS, Inc.
+//
+// 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 dlopen provides some convenience functions to dlopen a library and
+// get its symbols.
+package dlopen
+
+// #cgo LDFLAGS: -ldl
+// #include <stdlib.h>
+// #include <dlfcn.h>
+import "C"
+import (
+ "errors"
+ "fmt"
+ "unsafe"
+)
+
+var ErrSoNotFound = errors.New("unable to open a handle to the library")
+
+// LibHandle represents an open handle to a library (.so)
+type LibHandle struct {
+ Handle unsafe.Pointer
+ Libname string
+}
+
+// GetHandle tries to get a handle to a library (.so), attempting to access it
+// by the names specified in libs and returning the first that is successfully
+// opened. Callers are responsible for closing the handler. If no library can
+// be successfully opened, an error is returned.
+func GetHandle(libs []string) (*LibHandle, error) {
+ for _, name := range libs {
+ libname := C.CString(name)
+ defer C.free(unsafe.Pointer(libname))
+ handle := C.dlopen(libname, C.RTLD_LAZY)
+ if handle != nil {
+ h := &LibHandle{
+ Handle: handle,
+ Libname: name,
+ }
+ return h, nil
+ }
+ }
+ return nil, ErrSoNotFound
+}
+
+// GetSymbolPointer takes a symbol name and returns a pointer to the symbol.
+func (l *LibHandle) GetSymbolPointer(symbol string) (unsafe.Pointer, error) {
+ sym := C.CString(symbol)
+ defer C.free(unsafe.Pointer(sym))
+
+ C.dlerror()
+ p := C.dlsym(l.Handle, sym)
+ e := C.dlerror()
+ if e != nil {
+ return nil, fmt.Errorf("error resolving symbol %q: %v", symbol, errors.New(C.GoString(e)))
+ }
+
+ return p, nil
+}
+
+// Close closes a LibHandle.
+func (l *LibHandle) Close() error {
+ C.dlerror()
+ C.dlclose(l.Handle)
+ e := C.dlerror()
+ if e != nil {
+ return fmt.Errorf("error closing %v: %v", l.Libname, errors.New(C.GoString(e)))
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/coreos/go-systemd/v22/journal/journal.go b/vendor/github.com/coreos/go-systemd/v22/journal/journal.go
new file mode 100644
index 000000000..a0f4837a0
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/journal/journal.go
@@ -0,0 +1,225 @@
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 journal provides write bindings to the local systemd journal.
+// It is implemented in pure Go and connects to the journal directly over its
+// unix socket.
+//
+// To read from the journal, see the "sdjournal" package, which wraps the
+// sd-journal a C API.
+//
+// http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html
+package journal
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "syscall"
+ "unsafe"
+)
+
+// Priority of a journal message
+type Priority int
+
+const (
+ PriEmerg Priority = iota
+ PriAlert
+ PriCrit
+ PriErr
+ PriWarning
+ PriNotice
+ PriInfo
+ PriDebug
+)
+
+var (
+ // This can be overridden at build-time:
+ // https://github.com/golang/go/wiki/GcToolchainTricks#including-build-information-in-the-executable
+ journalSocket = "/run/systemd/journal/socket"
+
+ // unixConnPtr atomically holds the local unconnected Unix-domain socket.
+ // Concrete safe pointer type: *net.UnixConn
+ unixConnPtr unsafe.Pointer
+ // onceConn ensures that unixConnPtr is initialized exactly once.
+ onceConn sync.Once
+)
+
+func init() {
+ onceConn.Do(initConn)
+}
+
+// Enabled checks whether the local systemd journal is available for logging.
+func Enabled() bool {
+ onceConn.Do(initConn)
+
+ if (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr)) == nil {
+ return false
+ }
+
+ if _, err := net.Dial("unixgram", journalSocket); err != nil {
+ return false
+ }
+
+ return true
+}
+
+// Send a message to the local systemd journal. vars is a map of journald
+// fields to values. Fields must be composed of uppercase letters, numbers,
+// and underscores, but must not start with an underscore. Within these
+// restrictions, any arbitrary field name may be used. Some names have special
+// significance: see the journalctl documentation
+// (http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html)
+// for more details. vars may be nil.
+func Send(message string, priority Priority, vars map[string]string) error {
+ conn := (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr))
+ if conn == nil {
+ return errors.New("could not initialize socket to journald")
+ }
+
+ socketAddr := &net.UnixAddr{
+ Name: journalSocket,
+ Net: "unixgram",
+ }
+
+ data := new(bytes.Buffer)
+ appendVariable(data, "PRIORITY", strconv.Itoa(int(priority)))
+ appendVariable(data, "MESSAGE", message)
+ for k, v := range vars {
+ appendVariable(data, k, v)
+ }
+
+ _, _, err := conn.WriteMsgUnix(data.Bytes(), nil, socketAddr)
+ if err == nil {
+ return nil
+ }
+ if !isSocketSpaceError(err) {
+ return err
+ }
+
+ // Large log entry, send it via tempfile and ancillary-fd.
+ file, err := tempFd()
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+ _, err = io.Copy(file, data)
+ if err != nil {
+ return err
+ }
+ rights := syscall.UnixRights(int(file.Fd()))
+ _, _, err = conn.WriteMsgUnix([]byte{}, rights, socketAddr)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Print prints a message to the local systemd journal using Send().
+func Print(priority Priority, format string, a ...interface{}) error {
+ return Send(fmt.Sprintf(format, a...), priority, nil)
+}
+
+func appendVariable(w io.Writer, name, value string) {
+ if err := validVarName(name); err != nil {
+ fmt.Fprintf(os.Stderr, "variable name %s contains invalid character, ignoring\n", name)
+ }
+ if strings.ContainsRune(value, '\n') {
+ /* When the value contains a newline, we write:
+ * - the variable name, followed by a newline
+ * - the size (in 64bit little endian format)
+ * - the data, followed by a newline
+ */
+ fmt.Fprintln(w, name)
+ binary.Write(w, binary.LittleEndian, uint64(len(value)))
+ fmt.Fprintln(w, value)
+ } else {
+ /* just write the variable and value all on one line */
+ fmt.Fprintf(w, "%s=%s\n", name, value)
+ }
+}
+
+// validVarName validates a variable name to make sure journald will accept it.
+// The variable name must be in uppercase and consist only of characters,
+// numbers and underscores, and may not begin with an underscore:
+// https://www.freedesktop.org/software/systemd/man/sd_journal_print.html
+func validVarName(name string) error {
+ if name == "" {
+ return errors.New("Empty variable name")
+ } else if name[0] == '_' {
+ return errors.New("Variable name begins with an underscore")
+ }
+
+ for _, c := range name {
+ if !(('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_') {
+ return errors.New("Variable name contains invalid characters")
+ }
+ }
+ return nil
+}
+
+// isSocketSpaceError checks whether the error is signaling
+// an "overlarge message" condition.
+func isSocketSpaceError(err error) bool {
+ opErr, ok := err.(*net.OpError)
+ if !ok || opErr == nil {
+ return false
+ }
+
+ sysErr, ok := opErr.Err.(*os.SyscallError)
+ if !ok || sysErr == nil {
+ return false
+ }
+
+ return sysErr.Err == syscall.EMSGSIZE || sysErr.Err == syscall.ENOBUFS
+}
+
+// tempFd creates a temporary, unlinked file under `/dev/shm`.
+func tempFd() (*os.File, error) {
+ file, err := ioutil.TempFile("/dev/shm/", "journal.XXXXX")
+ if err != nil {
+ return nil, err
+ }
+ err = syscall.Unlink(file.Name())
+ if err != nil {
+ return nil, err
+ }
+ return file, nil
+}
+
+// initConn initializes the global `unixConnPtr` socket.
+// It is meant to be called exactly once, at program startup.
+func initConn() {
+ autobind, err := net.ResolveUnixAddr("unixgram", "")
+ if err != nil {
+ return
+ }
+
+ sock, err := net.ListenUnixgram("unixgram", autobind)
+ if err != nil {
+ return
+ }
+
+ atomic.StorePointer(&unixConnPtr, unsafe.Pointer(sock))
+}
diff --git a/vendor/github.com/coreos/go-systemd/v22/sdjournal/functions.go b/vendor/github.com/coreos/go-systemd/v22/sdjournal/functions.go
new file mode 100644
index 000000000..3cbd05658
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/sdjournal/functions.go
@@ -0,0 +1,66 @@
+// Copyright 2015 RedHat, Inc.
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 sdjournal
+
+import (
+ "github.com/coreos/go-systemd/v22/internal/dlopen"
+ "sync"
+ "unsafe"
+)
+
+var (
+ // lazy initialized
+ libsystemdHandle *dlopen.LibHandle
+
+ libsystemdMutex = &sync.Mutex{}
+ libsystemdFunctions = map[string]unsafe.Pointer{}
+ libsystemdNames = []string{
+ // systemd < 209
+ "libsystemd-journal.so.0",
+ "libsystemd-journal.so",
+
+ // systemd >= 209 merged libsystemd-journal into libsystemd proper
+ "libsystemd.so.0",
+ "libsystemd.so",
+ }
+)
+
+func getFunction(name string) (unsafe.Pointer, error) {
+ libsystemdMutex.Lock()
+ defer libsystemdMutex.Unlock()
+
+ if libsystemdHandle == nil {
+ h, err := dlopen.GetHandle(libsystemdNames)
+ if err != nil {
+ return nil, err
+ }
+
+ libsystemdHandle = h
+ }
+
+ f, ok := libsystemdFunctions[name]
+ if !ok {
+ var err error
+ f, err = libsystemdHandle.GetSymbolPointer(name)
+ if err != nil {
+ return nil, err
+ }
+
+ libsystemdFunctions[name] = f
+ }
+
+ return f, nil
+}
diff --git a/vendor/github.com/coreos/go-systemd/v22/sdjournal/journal.go b/vendor/github.com/coreos/go-systemd/v22/sdjournal/journal.go
new file mode 100644
index 000000000..7f840def8
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/sdjournal/journal.go
@@ -0,0 +1,1120 @@
+// Copyright 2015 RedHat, Inc.
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 sdjournal provides a low-level Go interface to the
+// systemd journal wrapped around the sd-journal C API.
+//
+// All public read methods map closely to the sd-journal API functions. See the
+// sd-journal.h documentation[1] for information about each function.
+//
+// To write to the journal, see the pure-Go "journal" package
+//
+// [1] http://www.freedesktop.org/software/systemd/man/sd-journal.html
+package sdjournal
+
+// #include <systemd/sd-journal.h>
+// #include <systemd/sd-id128.h>
+// #include <stdlib.h>
+// #include <syslog.h>
+//
+// int
+// my_sd_journal_open(void *f, sd_journal **ret, int flags)
+// {
+// int (*sd_journal_open)(sd_journal **, int);
+//
+// sd_journal_open = f;
+// return sd_journal_open(ret, flags);
+// }
+//
+// int
+// my_sd_journal_open_directory(void *f, sd_journal **ret, const char *path, int flags)
+// {
+// int (*sd_journal_open_directory)(sd_journal **, const char *, int);
+//
+// sd_journal_open_directory = f;
+// return sd_journal_open_directory(ret, path, flags);
+// }
+//
+// int
+// my_sd_journal_open_files(void *f, sd_journal **ret, const char **paths, int flags)
+// {
+// int (*sd_journal_open_files)(sd_journal **, const char **, int);
+//
+// sd_journal_open_files = f;
+// return sd_journal_open_files(ret, paths, flags);
+// }
+//
+// void
+// my_sd_journal_close(void *f, sd_journal *j)
+// {
+// int (*sd_journal_close)(sd_journal *);
+//
+// sd_journal_close = f;
+// sd_journal_close(j);
+// }
+//
+// int
+// my_sd_journal_get_usage(void *f, sd_journal *j, uint64_t *bytes)
+// {
+// int (*sd_journal_get_usage)(sd_journal *, uint64_t *);
+//
+// sd_journal_get_usage = f;
+// return sd_journal_get_usage(j, bytes);
+// }
+//
+// int
+// my_sd_journal_add_match(void *f, sd_journal *j, const void *data, size_t size)
+// {
+// int (*sd_journal_add_match)(sd_journal *, const void *, size_t);
+//
+// sd_journal_add_match = f;
+// return sd_journal_add_match(j, data, size);
+// }
+//
+// int
+// my_sd_journal_add_disjunction(void *f, sd_journal *j)
+// {
+// int (*sd_journal_add_disjunction)(sd_journal *);
+//
+// sd_journal_add_disjunction = f;
+// return sd_journal_add_disjunction(j);
+// }
+//
+// int
+// my_sd_journal_add_conjunction(void *f, sd_journal *j)
+// {
+// int (*sd_journal_add_conjunction)(sd_journal *);
+//
+// sd_journal_add_conjunction = f;
+// return sd_journal_add_conjunction(j);
+// }
+//
+// void
+// my_sd_journal_flush_matches(void *f, sd_journal *j)
+// {
+// int (*sd_journal_flush_matches)(sd_journal *);
+//
+// sd_journal_flush_matches = f;
+// sd_journal_flush_matches(j);
+// }
+//
+// int
+// my_sd_journal_next(void *f, sd_journal *j)
+// {
+// int (*sd_journal_next)(sd_journal *);
+//
+// sd_journal_next = f;
+// return sd_journal_next(j);
+// }
+//
+// int
+// my_sd_journal_next_skip(void *f, sd_journal *j, uint64_t skip)
+// {
+// int (*sd_journal_next_skip)(sd_journal *, uint64_t);
+//
+// sd_journal_next_skip = f;
+// return sd_journal_next_skip(j, skip);
+// }
+//
+// int
+// my_sd_journal_previous(void *f, sd_journal *j)
+// {
+// int (*sd_journal_previous)(sd_journal *);
+//
+// sd_journal_previous = f;
+// return sd_journal_previous(j);
+// }
+//
+// int
+// my_sd_journal_previous_skip(void *f, sd_journal *j, uint64_t skip)
+// {
+// int (*sd_journal_previous_skip)(sd_journal *, uint64_t);
+//
+// sd_journal_previous_skip = f;
+// return sd_journal_previous_skip(j, skip);
+// }
+//
+// int
+// my_sd_journal_get_data(void *f, sd_journal *j, const char *field, const void **data, size_t *length)
+// {
+// int (*sd_journal_get_data)(sd_journal *, const char *, const void **, size_t *);
+//
+// sd_journal_get_data = f;
+// return sd_journal_get_data(j, field, data, length);
+// }
+//
+// int
+// my_sd_journal_set_data_threshold(void *f, sd_journal *j, size_t sz)
+// {
+// int (*sd_journal_set_data_threshold)(sd_journal *, size_t);
+//
+// sd_journal_set_data_threshold = f;
+// return sd_journal_set_data_threshold(j, sz);
+// }
+//
+// int
+// my_sd_journal_get_cursor(void *f, sd_journal *j, char **cursor)
+// {
+// int (*sd_journal_get_cursor)(sd_journal *, char **);
+//
+// sd_journal_get_cursor = f;
+// return sd_journal_get_cursor(j, cursor);
+// }
+//
+// int
+// my_sd_journal_test_cursor(void *f, sd_journal *j, const char *cursor)
+// {
+// int (*sd_journal_test_cursor)(sd_journal *, const char *);
+//
+// sd_journal_test_cursor = f;
+// return sd_journal_test_cursor(j, cursor);
+// }
+//
+// int
+// my_sd_journal_get_realtime_usec(void *f, sd_journal *j, uint64_t *usec)
+// {
+// int (*sd_journal_get_realtime_usec)(sd_journal *, uint64_t *);
+//
+// sd_journal_get_realtime_usec = f;
+// return sd_journal_get_realtime_usec(j, usec);
+// }
+//
+// int
+// my_sd_journal_get_monotonic_usec(void *f, sd_journal *j, uint64_t *usec, sd_id128_t *boot_id)
+// {
+// int (*sd_journal_get_monotonic_usec)(sd_journal *, uint64_t *, sd_id128_t *);
+//
+// sd_journal_get_monotonic_usec = f;
+// return sd_journal_get_monotonic_usec(j, usec, boot_id);
+// }
+//
+// int
+// my_sd_journal_seek_head(void *f, sd_journal *j)
+// {
+// int (*sd_journal_seek_head)(sd_journal *);
+//
+// sd_journal_seek_head = f;
+// return sd_journal_seek_head(j);
+// }
+//
+// int
+// my_sd_journal_seek_tail(void *f, sd_journal *j)
+// {
+// int (*sd_journal_seek_tail)(sd_journal *);
+//
+// sd_journal_seek_tail = f;
+// return sd_journal_seek_tail(j);
+// }
+//
+//
+// int
+// my_sd_journal_seek_cursor(void *f, sd_journal *j, const char *cursor)
+// {
+// int (*sd_journal_seek_cursor)(sd_journal *, const char *);
+//
+// sd_journal_seek_cursor = f;
+// return sd_journal_seek_cursor(j, cursor);
+// }
+//
+// int
+// my_sd_journal_seek_realtime_usec(void *f, sd_journal *j, uint64_t usec)
+// {
+// int (*sd_journal_seek_realtime_usec)(sd_journal *, uint64_t);
+//
+// sd_journal_seek_realtime_usec = f;
+// return sd_journal_seek_realtime_usec(j, usec);
+// }
+//
+// int
+// my_sd_journal_wait(void *f, sd_journal *j, uint64_t timeout_usec)
+// {
+// int (*sd_journal_wait)(sd_journal *, uint64_t);
+//
+// sd_journal_wait = f;
+// return sd_journal_wait(j, timeout_usec);
+// }
+//
+// void
+// my_sd_journal_restart_data(void *f, sd_journal *j)
+// {
+// void (*sd_journal_restart_data)(sd_journal *);
+//
+// sd_journal_restart_data = f;
+// sd_journal_restart_data(j);
+// }
+//
+// int
+// my_sd_journal_enumerate_data(void *f, sd_journal *j, const void **data, size_t *length)
+// {
+// int (*sd_journal_enumerate_data)(sd_journal *, const void **, size_t *);
+//
+// sd_journal_enumerate_data = f;
+// return sd_journal_enumerate_data(j, data, length);
+// }
+//
+// int
+// my_sd_journal_query_unique(void *f, sd_journal *j, const char *field)
+// {
+// int(*sd_journal_query_unique)(sd_journal *, const char *);
+//
+// sd_journal_query_unique = f;
+// return sd_journal_query_unique(j, field);
+// }
+//
+// int
+// my_sd_journal_enumerate_unique(void *f, sd_journal *j, const void **data, size_t *length)
+// {
+// int(*sd_journal_enumerate_unique)(sd_journal *, const void **, size_t *);
+//
+// sd_journal_enumerate_unique = f;
+// return sd_journal_enumerate_unique(j, data, length);
+// }
+//
+// void
+// my_sd_journal_restart_unique(void *f, sd_journal *j)
+// {
+// void(*sd_journal_restart_unique)(sd_journal *);
+//
+// sd_journal_restart_unique = f;
+// sd_journal_restart_unique(j);
+// }
+//
+// int
+// my_sd_journal_get_catalog(void *f, sd_journal *j, char **ret)
+// {
+// int(*sd_journal_get_catalog)(sd_journal *, char **);
+//
+// sd_journal_get_catalog = f;
+// return sd_journal_get_catalog(j, ret);
+// }
+//
+import "C"
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "strings"
+ "sync"
+ "syscall"
+ "time"
+ "unsafe"
+)
+
+// Journal entry field strings which correspond to:
+// http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html
+const (
+ // User Journal Fields
+ SD_JOURNAL_FIELD_MESSAGE = "MESSAGE"
+ SD_JOURNAL_FIELD_MESSAGE_ID = "MESSAGE_ID"
+ SD_JOURNAL_FIELD_PRIORITY = "PRIORITY"
+ SD_JOURNAL_FIELD_CODE_FILE = "CODE_FILE"
+ SD_JOURNAL_FIELD_CODE_LINE = "CODE_LINE"
+ SD_JOURNAL_FIELD_CODE_FUNC = "CODE_FUNC"
+ SD_JOURNAL_FIELD_ERRNO = "ERRNO"
+ SD_JOURNAL_FIELD_SYSLOG_FACILITY = "SYSLOG_FACILITY"
+ SD_JOURNAL_FIELD_SYSLOG_IDENTIFIER = "SYSLOG_IDENTIFIER"
+ SD_JOURNAL_FIELD_SYSLOG_PID = "SYSLOG_PID"
+
+ // Trusted Journal Fields
+ SD_JOURNAL_FIELD_PID = "_PID"
+ SD_JOURNAL_FIELD_UID = "_UID"
+ SD_JOURNAL_FIELD_GID = "_GID"
+ SD_JOURNAL_FIELD_COMM = "_COMM"
+ SD_JOURNAL_FIELD_EXE = "_EXE"
+ SD_JOURNAL_FIELD_CMDLINE = "_CMDLINE"
+ SD_JOURNAL_FIELD_CAP_EFFECTIVE = "_CAP_EFFECTIVE"
+ SD_JOURNAL_FIELD_AUDIT_SESSION = "_AUDIT_SESSION"
+ SD_JOURNAL_FIELD_AUDIT_LOGINUID = "_AUDIT_LOGINUID"
+ SD_JOURNAL_FIELD_SYSTEMD_CGROUP = "_SYSTEMD_CGROUP"
+ SD_JOURNAL_FIELD_SYSTEMD_SESSION = "_SYSTEMD_SESSION"
+ SD_JOURNAL_FIELD_SYSTEMD_UNIT = "_SYSTEMD_UNIT"
+ SD_JOURNAL_FIELD_SYSTEMD_USER_UNIT = "_SYSTEMD_USER_UNIT"
+ SD_JOURNAL_FIELD_SYSTEMD_OWNER_UID = "_SYSTEMD_OWNER_UID"
+ SD_JOURNAL_FIELD_SYSTEMD_SLICE = "_SYSTEMD_SLICE"
+ SD_JOURNAL_FIELD_SELINUX_CONTEXT = "_SELINUX_CONTEXT"
+ SD_JOURNAL_FIELD_SOURCE_REALTIME_TIMESTAMP = "_SOURCE_REALTIME_TIMESTAMP"
+ SD_JOURNAL_FIELD_BOOT_ID = "_BOOT_ID"
+ SD_JOURNAL_FIELD_MACHINE_ID = "_MACHINE_ID"
+ SD_JOURNAL_FIELD_HOSTNAME = "_HOSTNAME"
+ SD_JOURNAL_FIELD_TRANSPORT = "_TRANSPORT"
+
+ // Address Fields
+ SD_JOURNAL_FIELD_CURSOR = "__CURSOR"
+ SD_JOURNAL_FIELD_REALTIME_TIMESTAMP = "__REALTIME_TIMESTAMP"
+ SD_JOURNAL_FIELD_MONOTONIC_TIMESTAMP = "__MONOTONIC_TIMESTAMP"
+)
+
+// Journal event constants
+const (
+ SD_JOURNAL_NOP = int(C.SD_JOURNAL_NOP)
+ SD_JOURNAL_APPEND = int(C.SD_JOURNAL_APPEND)
+ SD_JOURNAL_INVALIDATE = int(C.SD_JOURNAL_INVALIDATE)
+)
+
+const (
+ // IndefiniteWait is a sentinel value that can be passed to
+ // sdjournal.Wait() to signal an indefinite wait for new journal
+ // events. It is implemented as the maximum value for a time.Duration:
+ // https://github.com/golang/go/blob/e4dcf5c8c22d98ac9eac7b9b226596229624cb1d/src/time/time.go#L434
+ IndefiniteWait time.Duration = 1<<63 - 1
+)
+
+var (
+ // ErrNoTestCursor gets returned when using TestCursor function and cursor
+ // parameter is not the same as the current cursor position.
+ ErrNoTestCursor = errors.New("Cursor parameter is not the same as current position")
+)
+
+// Journal is a Go wrapper of an sd_journal structure.
+type Journal struct {
+ cjournal *C.sd_journal
+ mu sync.Mutex
+}
+
+// JournalEntry represents all fields of a journal entry plus address fields.
+type JournalEntry struct {
+ Fields map[string]string
+ Cursor string
+ RealtimeTimestamp uint64
+ MonotonicTimestamp uint64
+}
+
+// Match is a convenience wrapper to describe filters supplied to AddMatch.
+type Match struct {
+ Field string
+ Value string
+}
+
+// String returns a string representation of a Match suitable for use with AddMatch.
+func (m *Match) String() string {
+ return m.Field + "=" + m.Value
+}
+
+// NewJournal returns a new Journal instance pointing to the local journal
+func NewJournal() (j *Journal, err error) {
+ j = &Journal{}
+
+ sd_journal_open, err := getFunction("sd_journal_open")
+ if err != nil {
+ return nil, err
+ }
+
+ r := C.my_sd_journal_open(sd_journal_open, &j.cjournal, C.SD_JOURNAL_LOCAL_ONLY)
+
+ if r < 0 {
+ return nil, fmt.Errorf("failed to open journal: %s", syscall.Errno(-r).Error())
+ }
+
+ return j, nil
+}
+
+// NewJournalFromDir returns a new Journal instance pointing to a journal residing
+// in a given directory.
+func NewJournalFromDir(path string) (j *Journal, err error) {
+ j = &Journal{}
+
+ sd_journal_open_directory, err := getFunction("sd_journal_open_directory")
+ if err != nil {
+ return nil, err
+ }
+
+ p := C.CString(path)
+ defer C.free(unsafe.Pointer(p))
+
+ r := C.my_sd_journal_open_directory(sd_journal_open_directory, &j.cjournal, p, 0)
+ if r < 0 {
+ return nil, fmt.Errorf("failed to open journal in directory %q: %s", path, syscall.Errno(-r).Error())
+ }
+
+ return j, nil
+}
+
+// NewJournalFromFiles returns a new Journal instance pointing to a journals residing
+// in a given files.
+func NewJournalFromFiles(paths ...string) (j *Journal, err error) {
+ j = &Journal{}
+
+ sd_journal_open_files, err := getFunction("sd_journal_open_files")
+ if err != nil {
+ return nil, err
+ }
+
+ // by making the slice 1 elem too long, we guarantee it'll be null-terminated
+ cPaths := make([]*C.char, len(paths)+1)
+ for idx, path := range paths {
+ p := C.CString(path)
+ cPaths[idx] = p
+ defer C.free(unsafe.Pointer(p))
+ }
+
+ r := C.my_sd_journal_open_files(sd_journal_open_files, &j.cjournal, &cPaths[0], 0)
+ if r < 0 {
+ return nil, fmt.Errorf("failed to open journals in paths %q: %s", paths, syscall.Errno(-r).Error())
+ }
+
+ return j, nil
+}
+
+// Close closes a journal opened with NewJournal.
+func (j *Journal) Close() error {
+ sd_journal_close, err := getFunction("sd_journal_close")
+ if err != nil {
+ return err
+ }
+
+ j.mu.Lock()
+ C.my_sd_journal_close(sd_journal_close, j.cjournal)
+ j.mu.Unlock()
+
+ return nil
+}
+
+// AddMatch adds a match by which to filter the entries of the journal.
+func (j *Journal) AddMatch(match string) error {
+ sd_journal_add_match, err := getFunction("sd_journal_add_match")
+ if err != nil {
+ return err
+ }
+
+ m := C.CString(match)
+ defer C.free(unsafe.Pointer(m))
+
+ j.mu.Lock()
+ r := C.my_sd_journal_add_match(sd_journal_add_match, j.cjournal, unsafe.Pointer(m), C.size_t(len(match)))
+ j.mu.Unlock()
+
+ if r < 0 {
+ return fmt.Errorf("failed to add match: %s", syscall.Errno(-r).Error())
+ }
+
+ return nil
+}
+
+// AddDisjunction inserts a logical OR in the match list.
+func (j *Journal) AddDisjunction() error {
+ sd_journal_add_disjunction, err := getFunction("sd_journal_add_disjunction")
+ if err != nil {
+ return err
+ }
+
+ j.mu.Lock()
+ r := C.my_sd_journal_add_disjunction(sd_journal_add_disjunction, j.cjournal)
+ j.mu.Unlock()
+
+ if r < 0 {
+ return fmt.Errorf("failed to add a disjunction in the match list: %s", syscall.Errno(-r).Error())
+ }
+
+ return nil
+}
+
+// AddConjunction inserts a logical AND in the match list.
+func (j *Journal) AddConjunction() error {
+ sd_journal_add_conjunction, err := getFunction("sd_journal_add_conjunction")
+ if err != nil {
+ return err
+ }
+
+ j.mu.Lock()
+ r := C.my_sd_journal_add_conjunction(sd_journal_add_conjunction, j.cjournal)
+ j.mu.Unlock()
+
+ if r < 0 {
+ return fmt.Errorf("failed to add a conjunction in the match list: %s", syscall.Errno(-r).Error())
+ }
+
+ return nil
+}
+
+// FlushMatches flushes all matches, disjunctions and conjunctions.
+func (j *Journal) FlushMatches() {
+ sd_journal_flush_matches, err := getFunction("sd_journal_flush_matches")
+ if err != nil {
+ return
+ }
+
+ j.mu.Lock()
+ C.my_sd_journal_flush_matches(sd_journal_flush_matches, j.cjournal)
+ j.mu.Unlock()
+}
+
+// Next advances the read pointer into the journal by one entry.
+func (j *Journal) Next() (uint64, error) {
+ sd_journal_next, err := getFunction("sd_journal_next")
+ if err != nil {
+ return 0, err
+ }
+
+ j.mu.Lock()
+ r := C.my_sd_journal_next(sd_journal_next, j.cjournal)
+ j.mu.Unlock()
+
+ if r < 0 {
+ return 0, fmt.Errorf("failed to iterate journal: %s", syscall.Errno(-r).Error())
+ }
+
+ return uint64(r), nil
+}
+
+// NextSkip advances the read pointer by multiple entries at once,
+// as specified by the skip parameter.
+func (j *Journal) NextSkip(skip uint64) (uint64, error) {
+ sd_journal_next_skip, err := getFunction("sd_journal_next_skip")
+ if err != nil {
+ return 0, err
+ }
+
+ j.mu.Lock()
+ r := C.my_sd_journal_next_skip(sd_journal_next_skip, j.cjournal, C.uint64_t(skip))
+ j.mu.Unlock()
+
+ if r < 0 {
+ return 0, fmt.Errorf("failed to iterate journal: %s", syscall.Errno(-r).Error())
+ }
+
+ return uint64(r), nil
+}
+
+// Previous sets the read pointer into the journal back by one entry.
+func (j *Journal) Previous() (uint64, error) {
+ sd_journal_previous, err := getFunction("sd_journal_previous")
+ if err != nil {
+ return 0, err
+ }
+
+ j.mu.Lock()
+ r := C.my_sd_journal_previous(sd_journal_previous, j.cjournal)
+ j.mu.Unlock()
+
+ if r < 0 {
+ return 0, fmt.Errorf("failed to iterate journal: %s", syscall.Errno(-r).Error())
+ }
+
+ return uint64(r), nil
+}
+
+// PreviousSkip sets back the read pointer by multiple entries at once,
+// as specified by the skip parameter.
+func (j *Journal) PreviousSkip(skip uint64) (uint64, error) {
+ sd_journal_previous_skip, err := getFunction("sd_journal_previous_skip")
+ if err != nil {
+ return 0, err
+ }
+
+ j.mu.Lock()
+ r := C.my_sd_journal_previous_skip(sd_journal_previous_skip, j.cjournal, C.uint64_t(skip))
+ j.mu.Unlock()
+
+ if r < 0 {
+ return 0, fmt.Errorf("failed to iterate journal: %s", syscall.Errno(-r).Error())
+ }
+
+ return uint64(r), nil
+}
+
+func (j *Journal) getData(field string) (unsafe.Pointer, C.int, error) {
+ sd_journal_get_data, err := getFunction("sd_journal_get_data")
+ if err != nil {
+ return nil, 0, err
+ }
+
+ f := C.CString(field)
+ defer C.free(unsafe.Pointer(f))
+
+ var d unsafe.Pointer
+ var l C.size_t
+
+ j.mu.Lock()
+ r := C.my_sd_journal_get_data(sd_journal_get_data, j.cjournal, f, &d, &l)
+ j.mu.Unlock()
+
+ if r < 0 {
+ return nil, 0, fmt.Errorf("failed to read message: %s", syscall.Errno(-r).Error())
+ }
+
+ return d, C.int(l), nil
+}
+
+// GetData gets the data object associated with a specific field from the
+// the journal entry referenced by the last completed Next/Previous function
+// call. To call GetData, you must have first called one of these functions.
+func (j *Journal) GetData(field string) (string, error) {
+ d, l, err := j.getData(field)
+ if err != nil {
+ return "", err
+ }
+
+ return C.GoStringN((*C.char)(d), l), nil
+}
+
+// GetDataValue gets the data object associated with a specific field from the
+// journal entry referenced by the last completed Next/Previous function call,
+// returning only the value of the object. To call GetDataValue, you must first
+// have called one of the Next/Previous functions.
+func (j *Journal) GetDataValue(field string) (string, error) {
+ val, err := j.GetData(field)
+ if err != nil {
+ return "", err
+ }
+
+ return strings.SplitN(val, "=", 2)[1], nil
+}
+
+// GetDataBytes gets the data object associated with a specific field from the
+// journal entry referenced by the last completed Next/Previous function call.
+// To call GetDataBytes, you must first have called one of these functions.
+func (j *Journal) GetDataBytes(field string) ([]byte, error) {
+ d, l, err := j.getData(field)
+ if err != nil {
+ return nil, err
+ }
+
+ return C.GoBytes(d, l), nil
+}
+
+// GetDataValueBytes gets the data object associated with a specific field from the
+// journal entry referenced by the last completed Next/Previous function call,
+// returning only the value of the object. To call GetDataValueBytes, you must first
+// have called one of the Next/Previous functions.
+func (j *Journal) GetDataValueBytes(field string) ([]byte, error) {
+ val, err := j.GetDataBytes(field)
+ if err != nil {
+ return nil, err
+ }
+
+ return bytes.SplitN(val, []byte("="), 2)[1], nil
+}
+
+// GetEntry returns a full representation of the journal entry referenced by the
+// last completed Next/Previous function call, with all key-value pairs of data
+// as well as address fields (cursor, realtime timestamp and monotonic timestamp).
+// To call GetEntry, you must first have called one of the Next/Previous functions.
+func (j *Journal) GetEntry() (*JournalEntry, error) {
+ sd_journal_get_realtime_usec, err := getFunction("sd_journal_get_realtime_usec")
+ if err != nil {
+ return nil, err
+ }
+
+ sd_journal_get_monotonic_usec, err := getFunction("sd_journal_get_monotonic_usec")
+ if err != nil {
+ return nil, err
+ }
+
+ sd_journal_get_cursor, err := getFunction("sd_journal_get_cursor")
+ if err != nil {
+ return nil, err
+ }
+
+ sd_journal_restart_data, err := getFunction("sd_journal_restart_data")
+ if err != nil {
+ return nil, err
+ }
+
+ sd_journal_enumerate_data, err := getFunction("sd_journal_enumerate_data")
+ if err != nil {
+ return nil, err
+ }
+
+ j.mu.Lock()
+ defer j.mu.Unlock()
+
+ var r C.int
+ entry := &JournalEntry{Fields: make(map[string]string)}
+
+ var realtimeUsec C.uint64_t
+ r = C.my_sd_journal_get_realtime_usec(sd_journal_get_realtime_usec, j.cjournal, &realtimeUsec)
+ if r < 0 {
+ return nil, fmt.Errorf("failed to get realtime timestamp: %s", syscall.Errno(-r).Error())
+ }
+
+ entry.RealtimeTimestamp = uint64(realtimeUsec)
+
+ var monotonicUsec C.uint64_t
+ var boot_id C.sd_id128_t
+
+ r = C.my_sd_journal_get_monotonic_usec(sd_journal_get_monotonic_usec, j.cjournal, &monotonicUsec, &boot_id)
+ if r < 0 {
+ return nil, fmt.Errorf("failed to get monotonic timestamp: %s", syscall.Errno(-r).Error())
+ }
+
+ entry.MonotonicTimestamp = uint64(monotonicUsec)
+
+ var c *C.char
+ // since the pointer is mutated by sd_journal_get_cursor, need to wait
+ // until after the call to free the memory
+ r = C.my_sd_journal_get_cursor(sd_journal_get_cursor, j.cjournal, &c)
+ defer C.free(unsafe.Pointer(c))
+ if r < 0 {
+ return nil, fmt.Errorf("failed to get cursor: %s", syscall.Errno(-r).Error())
+ }
+
+ entry.Cursor = C.GoString(c)
+
+ // Implements the JOURNAL_FOREACH_DATA_RETVAL macro from journal-internal.h
+ var d unsafe.Pointer
+ var l C.size_t
+ C.my_sd_journal_restart_data(sd_journal_restart_data, j.cjournal)
+ for {
+ r = C.my_sd_journal_enumerate_data(sd_journal_enumerate_data, j.cjournal, &d, &l)
+ if r == 0 {
+ break
+ }
+
+ if r < 0 {
+ return nil, fmt.Errorf("failed to read message field: %s", syscall.Errno(-r).Error())
+ }
+
+ msg := C.GoStringN((*C.char)(d), C.int(l))
+ kv := strings.SplitN(msg, "=", 2)
+ if len(kv) < 2 {
+ return nil, fmt.Errorf("failed to parse field")
+ }
+
+ entry.Fields[kv[0]] = kv[1]
+ }
+
+ return entry, nil
+}
+
+// SetDataThreshold sets the data field size threshold for data returned by
+// GetData. To retrieve the complete data fields this threshold should be
+// turned off by setting it to 0, so that the library always returns the
+// complete data objects.
+func (j *Journal) SetDataThreshold(threshold uint64) error {
+ sd_journal_set_data_threshold, err := getFunction("sd_journal_set_data_threshold")
+ if err != nil {
+ return err
+ }
+
+ j.mu.Lock()
+ r := C.my_sd_journal_set_data_threshold(sd_journal_set_data_threshold, j.cjournal, C.size_t(threshold))
+ j.mu.Unlock()
+
+ if r < 0 {
+ return fmt.Errorf("failed to set data threshold: %s", syscall.Errno(-r).Error())
+ }
+
+ return nil
+}
+
+// GetRealtimeUsec gets the realtime (wallclock) timestamp of the journal
+// entry referenced by the last completed Next/Previous function call. To
+// call GetRealtimeUsec, you must first have called one of the Next/Previous
+// functions.
+func (j *Journal) GetRealtimeUsec() (uint64, error) {
+ var usec C.uint64_t
+
+ sd_journal_get_realtime_usec, err := getFunction("sd_journal_get_realtime_usec")
+ if err != nil {
+ return 0, err
+ }
+
+ j.mu.Lock()
+ r := C.my_sd_journal_get_realtime_usec(sd_journal_get_realtime_usec, j.cjournal, &usec)
+ j.mu.Unlock()
+
+ if r < 0 {
+ return 0, fmt.Errorf("failed to get realtime timestamp: %s", syscall.Errno(-r).Error())
+ }
+
+ return uint64(usec), nil
+}
+
+// GetMonotonicUsec gets the monotonic timestamp of the journal entry
+// referenced by the last completed Next/Previous function call. To call
+// GetMonotonicUsec, you must first have called one of the Next/Previous
+// functions.
+func (j *Journal) GetMonotonicUsec() (uint64, error) {
+ var usec C.uint64_t
+ var boot_id C.sd_id128_t
+
+ sd_journal_get_monotonic_usec, err := getFunction("sd_journal_get_monotonic_usec")
+ if err != nil {
+ return 0, err
+ }
+
+ j.mu.Lock()
+ r := C.my_sd_journal_get_monotonic_usec(sd_journal_get_monotonic_usec, j.cjournal, &usec, &boot_id)
+ j.mu.Unlock()
+
+ if r < 0 {
+ return 0, fmt.Errorf("failed to get monotonic timestamp: %s", syscall.Errno(-r).Error())
+ }
+
+ return uint64(usec), nil
+}
+
+// GetCursor gets the cursor of the last journal entry reeferenced by the
+// last completed Next/Previous function call. To call GetCursor, you must
+// first have called one of the Next/Previous functions.
+func (j *Journal) GetCursor() (string, error) {
+ sd_journal_get_cursor, err := getFunction("sd_journal_get_cursor")
+ if err != nil {
+ return "", err
+ }
+
+ var d *C.char
+ // since the pointer is mutated by sd_journal_get_cursor, need to wait
+ // until after the call to free the memory
+
+ j.mu.Lock()
+ r := C.my_sd_journal_get_cursor(sd_journal_get_cursor, j.cjournal, &d)
+ j.mu.Unlock()
+ defer C.free(unsafe.Pointer(d))
+
+ if r < 0 {
+ return "", fmt.Errorf("failed to get cursor: %s", syscall.Errno(-r).Error())
+ }
+
+ cursor := C.GoString(d)
+
+ return cursor, nil
+}
+
+// TestCursor checks whether the current position in the journal matches the
+// specified cursor
+func (j *Journal) TestCursor(cursor string) error {
+ sd_journal_test_cursor, err := getFunction("sd_journal_test_cursor")
+ if err != nil {
+ return err
+ }
+
+ c := C.CString(cursor)
+ defer C.free(unsafe.Pointer(c))
+
+ j.mu.Lock()
+ r := C.my_sd_journal_test_cursor(sd_journal_test_cursor, j.cjournal, c)
+ j.mu.Unlock()
+
+ if r < 0 {
+ return fmt.Errorf("failed to test to cursor %q: %s", cursor, syscall.Errno(-r).Error())
+ } else if r == 0 {
+ return ErrNoTestCursor
+ }
+
+ return nil
+}
+
+// SeekHead seeks to the beginning of the journal, i.e. the oldest available
+// entry. This call must be followed by a call to Next before any call to
+// Get* will return data about the first element.
+func (j *Journal) SeekHead() error {
+ sd_journal_seek_head, err := getFunction("sd_journal_seek_head")
+ if err != nil {
+ return err
+ }
+
+ j.mu.Lock()
+ r := C.my_sd_journal_seek_head(sd_journal_seek_head, j.cjournal)
+ j.mu.Unlock()
+
+ if r < 0 {
+ return fmt.Errorf("failed to seek to head of journal: %s", syscall.Errno(-r).Error())
+ }
+
+ return nil
+}
+
+// SeekTail may be used to seek to the end of the journal, i.e. the most recent
+// available entry. This call must be followed by a call to Next before any
+// call to Get* will return data about the last element.
+func (j *Journal) SeekTail() error {
+ sd_journal_seek_tail, err := getFunction("sd_journal_seek_tail")
+ if err != nil {
+ return err
+ }
+
+ j.mu.Lock()
+ r := C.my_sd_journal_seek_tail(sd_journal_seek_tail, j.cjournal)
+ j.mu.Unlock()
+
+ if r < 0 {
+ return fmt.Errorf("failed to seek to tail of journal: %s", syscall.Errno(-r).Error())
+ }
+
+ return nil
+}
+
+// SeekRealtimeUsec seeks to the entry with the specified realtime (wallclock)
+// timestamp, i.e. CLOCK_REALTIME. This call must be followed by a call to
+// Next/Previous before any call to Get* will return data about the sought entry.
+func (j *Journal) SeekRealtimeUsec(usec uint64) error {
+ sd_journal_seek_realtime_usec, err := getFunction("sd_journal_seek_realtime_usec")
+ if err != nil {
+ return err
+ }
+
+ j.mu.Lock()
+ r := C.my_sd_journal_seek_realtime_usec(sd_journal_seek_realtime_usec, j.cjournal, C.uint64_t(usec))
+ j.mu.Unlock()
+
+ if r < 0 {
+ return fmt.Errorf("failed to seek to %d: %s", usec, syscall.Errno(-r).Error())
+ }
+
+ return nil
+}
+
+// SeekCursor seeks to a concrete journal cursor. This call must be
+// followed by a call to Next/Previous before any call to Get* will return
+// data about the sought entry.
+func (j *Journal) SeekCursor(cursor string) error {
+ sd_journal_seek_cursor, err := getFunction("sd_journal_seek_cursor")
+ if err != nil {
+ return err
+ }
+
+ c := C.CString(cursor)
+ defer C.free(unsafe.Pointer(c))
+
+ j.mu.Lock()
+ r := C.my_sd_journal_seek_cursor(sd_journal_seek_cursor, j.cjournal, c)
+ j.mu.Unlock()
+
+ if r < 0 {
+ return fmt.Errorf("failed to seek to cursor %q: %s", cursor, syscall.Errno(-r).Error())
+ }
+
+ return nil
+}
+
+// Wait will synchronously wait until the journal gets changed. The maximum time
+// this call sleeps may be controlled with the timeout parameter. If
+// sdjournal.IndefiniteWait is passed as the timeout parameter, Wait will
+// wait indefinitely for a journal change.
+func (j *Journal) Wait(timeout time.Duration) int {
+ var to uint64
+
+ sd_journal_wait, err := getFunction("sd_journal_wait")
+ if err != nil {
+ return -1
+ }
+
+ if timeout == IndefiniteWait {
+ // sd_journal_wait(3) calls for a (uint64_t) -1 to be passed to signify
+ // indefinite wait, but using a -1 overflows our C.uint64_t, so we use an
+ // equivalent hex value.
+ to = 0xffffffffffffffff
+ } else {
+ to = uint64(timeout / time.Microsecond)
+ }
+ j.mu.Lock()
+ r := C.my_sd_journal_wait(sd_journal_wait, j.cjournal, C.uint64_t(to))
+ j.mu.Unlock()
+
+ return int(r)
+}
+
+// GetUsage returns the journal disk space usage, in bytes.
+func (j *Journal) GetUsage() (uint64, error) {
+ var out C.uint64_t
+
+ sd_journal_get_usage, err := getFunction("sd_journal_get_usage")
+ if err != nil {
+ return 0, err
+ }
+
+ j.mu.Lock()
+ r := C.my_sd_journal_get_usage(sd_journal_get_usage, j.cjournal, &out)
+ j.mu.Unlock()
+
+ if r < 0 {
+ return 0, fmt.Errorf("failed to get journal disk space usage: %s", syscall.Errno(-r).Error())
+ }
+
+ return uint64(out), nil
+}
+
+// GetUniqueValues returns all unique values for a given field.
+func (j *Journal) GetUniqueValues(field string) ([]string, error) {
+ var result []string
+
+ sd_journal_query_unique, err := getFunction("sd_journal_query_unique")
+ if err != nil {
+ return nil, err
+ }
+
+ sd_journal_enumerate_unique, err := getFunction("sd_journal_enumerate_unique")
+ if err != nil {
+ return nil, err
+ }
+
+ sd_journal_restart_unique, err := getFunction("sd_journal_restart_unique")
+ if err != nil {
+ return nil, err
+ }
+
+ j.mu.Lock()
+ defer j.mu.Unlock()
+
+ f := C.CString(field)
+ defer C.free(unsafe.Pointer(f))
+
+ r := C.my_sd_journal_query_unique(sd_journal_query_unique, j.cjournal, f)
+
+ if r < 0 {
+ return nil, fmt.Errorf("failed to query journal: %s", syscall.Errno(-r).Error())
+ }
+
+ // Implements the SD_JOURNAL_FOREACH_UNIQUE macro from sd-journal.h
+ var d unsafe.Pointer
+ var l C.size_t
+ C.my_sd_journal_restart_unique(sd_journal_restart_unique, j.cjournal)
+ for {
+ r = C.my_sd_journal_enumerate_unique(sd_journal_enumerate_unique, j.cjournal, &d, &l)
+ if r == 0 {
+ break
+ }
+
+ if r < 0 {
+ return nil, fmt.Errorf("failed to read message field: %s", syscall.Errno(-r).Error())
+ }
+
+ msg := C.GoStringN((*C.char)(d), C.int(l))
+ kv := strings.SplitN(msg, "=", 2)
+ if len(kv) < 2 {
+ return nil, fmt.Errorf("failed to parse field")
+ }
+
+ result = append(result, kv[1])
+ }
+
+ return result, nil
+}
+
+// GetCatalog retrieves a message catalog entry for the journal entry referenced
+// by the last completed Next/Previous function call. To call GetCatalog, you
+// must first have called one of these functions.
+func (j *Journal) GetCatalog() (string, error) {
+ sd_journal_get_catalog, err := getFunction("sd_journal_get_catalog")
+ if err != nil {
+ return "", err
+ }
+
+ var c *C.char
+
+ j.mu.Lock()
+ r := C.my_sd_journal_get_catalog(sd_journal_get_catalog, j.cjournal, &c)
+ j.mu.Unlock()
+ defer C.free(unsafe.Pointer(c))
+
+ if r < 0 {
+ return "", fmt.Errorf("failed to retrieve catalog entry for current journal entry: %s", syscall.Errno(-r).Error())
+ }
+
+ catalog := C.GoString(c)
+
+ return catalog, nil
+}
diff --git a/vendor/github.com/coreos/go-systemd/v22/sdjournal/read.go b/vendor/github.com/coreos/go-systemd/v22/sdjournal/read.go
new file mode 100644
index 000000000..51a060fb5
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/v22/sdjournal/read.go
@@ -0,0 +1,272 @@
+// Copyright 2015 RedHat, Inc.
+// Copyright 2015 CoreOS, Inc.
+//
+// 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 sdjournal
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "strings"
+ "sync"
+ "time"
+)
+
+var (
+ // ErrExpired gets returned when the Follow function runs into the
+ // specified timeout.
+ ErrExpired = errors.New("Timeout expired")
+)
+
+// JournalReaderConfig represents options to drive the behavior of a JournalReader.
+type JournalReaderConfig struct {
+ // The Since, NumFromTail and Cursor options are mutually exclusive and
+ // determine where the reading begins within the journal. The order in which
+ // options are written is exactly the order of precedence.
+ Since time.Duration // start relative to a Duration from now
+ NumFromTail uint64 // start relative to the tail
+ Cursor string // start relative to the cursor
+
+ // Show only journal entries whose fields match the supplied values. If
+ // the array is empty, entries will not be filtered.
+ Matches []Match
+
+ // If not empty, the journal instance will point to a journal residing
+ // in this directory. The supplied path may be relative or absolute.
+ Path string
+
+ // If not nil, Formatter will be used to translate the resulting entries
+ // into strings. If not set, the default format (timestamp and message field)
+ // will be used. If Formatter returns an error, Read will stop and return the error.
+ Formatter func(entry *JournalEntry) (string, error)
+}
+
+// JournalReader is an io.ReadCloser which provides a simple interface for iterating through the
+// systemd journal. A JournalReader is not safe for concurrent use by multiple goroutines.
+type JournalReader struct {
+ journal *Journal
+ msgReader *strings.Reader
+ formatter func(entry *JournalEntry) (string, error)
+}
+
+// NewJournalReader creates a new JournalReader with configuration options that are similar to the
+// systemd journalctl tool's iteration and filtering features.
+func NewJournalReader(config JournalReaderConfig) (*JournalReader, error) {
+ // use simpleMessageFormatter as default formatter.
+ if config.Formatter == nil {
+ config.Formatter = simpleMessageFormatter
+ }
+
+ r := &JournalReader{
+ formatter: config.Formatter,
+ }
+
+ // Open the journal
+ var err error
+ if config.Path != "" {
+ r.journal, err = NewJournalFromDir(config.Path)
+ } else {
+ r.journal, err = NewJournal()
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ // Add any supplied matches
+ for _, m := range config.Matches {
+ if err = r.journal.AddMatch(m.String()); err != nil {
+ return nil, err
+ }
+ }
+
+ // Set the start position based on options
+ if config.Since != 0 {
+ // Start based on a relative time
+ start := time.Now().Add(config.Since)
+ if err := r.journal.SeekRealtimeUsec(uint64(start.UnixNano() / 1000)); err != nil {
+ return nil, err
+ }
+ } else if config.NumFromTail != 0 {
+ // Start based on a number of lines before the tail
+ if err := r.journal.SeekTail(); err != nil {
+ return nil, err
+ }
+
+ // Move the read pointer into position near the tail. Go one further than
+ // the option so that the initial cursor advancement positions us at the
+ // correct starting point.
+ skip, err := r.journal.PreviousSkip(config.NumFromTail + 1)
+ if err != nil {
+ return nil, err
+ }
+ // If we skipped fewer lines than expected, we have reached journal start.
+ // Thus, we seek to head so that next invocation can read the first line.
+ if skip != config.NumFromTail+1 {
+ if err := r.journal.SeekHead(); err != nil {
+ return nil, err
+ }
+ }
+ } else if config.Cursor != "" {
+ // Start based on a custom cursor
+ if err := r.journal.SeekCursor(config.Cursor); err != nil {
+ return nil, err
+ }
+ }
+
+ return r, nil
+}
+
+// Read reads entries from the journal. Read follows the Reader interface so
+// it must be able to read a specific amount of bytes. Journald on the other
+// hand only allows us to read full entries of arbitrary size (without byte
+// granularity). JournalReader is therefore internally buffering entries that
+// don't fit in the read buffer. Callers should keep calling until 0 and/or an
+// error is returned.
+func (r *JournalReader) Read(b []byte) (int, error) {
+ if r.msgReader == nil {
+ // Advance the journal cursor. It has to be called at least one time
+ // before reading
+ c, err := r.journal.Next()
+
+ // An unexpected error
+ if err != nil {
+ return 0, err
+ }
+
+ // EOF detection
+ if c == 0 {
+ return 0, io.EOF
+ }
+
+ entry, err := r.journal.GetEntry()
+ if err != nil {
+ return 0, err
+ }
+
+ // Build a message
+ msg, err := r.formatter(entry)
+ if err != nil {
+ return 0, err
+ }
+ r.msgReader = strings.NewReader(msg)
+ }
+
+ // Copy and return the message
+ sz, err := r.msgReader.Read(b)
+ if err == io.EOF {
+ // The current entry has been fully read. Don't propagate this
+ // EOF, so the next entry can be read at the next Read()
+ // iteration.
+ r.msgReader = nil
+ return sz, nil
+ }
+ if err != nil {
+ return sz, err
+ }
+ if r.msgReader.Len() == 0 {
+ r.msgReader = nil
+ }
+
+ return sz, nil
+}
+
+// Close closes the JournalReader's handle to the journal.
+func (r *JournalReader) Close() error {
+ return r.journal.Close()
+}
+
+// Rewind attempts to rewind the JournalReader to the first entry.
+func (r *JournalReader) Rewind() error {
+ r.msgReader = nil
+ return r.journal.SeekHead()
+}
+
+// Follow synchronously follows the JournalReader, writing each new journal entry to writer. The
+// follow will continue until a single time.Time is received on the until channel.
+func (r *JournalReader) Follow(until <-chan time.Time, writer io.Writer) error {
+
+ // Process journal entries and events. Entries are flushed until the tail or
+ // timeout is reached, and then we wait for new events or the timeout.
+ var msg = make([]byte, 64*1<<(10))
+ var waitCh = make(chan int, 1)
+ var waitGroup sync.WaitGroup
+ defer waitGroup.Wait()
+
+process:
+ for {
+ c, err := r.Read(msg)
+ if err != nil && err != io.EOF {
+ return err
+ }
+
+ select {
+ case <-until:
+ return ErrExpired
+ default:
+ }
+ if c > 0 {
+ if _, err = writer.Write(msg[:c]); err != nil {
+ return err
+ }
+ continue process
+ }
+
+ // We're at the tail, so wait for new events or time out.
+ // Holds journal events to process. Tightly bounded for now unless there's a
+ // reason to unblock the journal watch routine more quickly.
+ for {
+ waitGroup.Add(1)
+ go func() {
+ status := r.journal.Wait(100 * time.Millisecond)
+ waitCh <- status
+ waitGroup.Done()
+ }()
+
+ select {
+ case <-until:
+ return ErrExpired
+ case e := <-waitCh:
+ switch e {
+ case SD_JOURNAL_NOP:
+ // the journal did not change since the last invocation
+ case SD_JOURNAL_APPEND, SD_JOURNAL_INVALIDATE:
+ continue process
+ default:
+ if e < 0 {
+ return fmt.Errorf("received error event: %d", e)
+ }
+
+ log.Printf("received unknown event: %d\n", e)
+ }
+ }
+ }
+ }
+}
+
+// simpleMessageFormatter is the default formatter.
+// It returns a string representing the current journal entry in a simple format which
+// includes the entry timestamp and MESSAGE field.
+func simpleMessageFormatter(entry *JournalEntry) (string, error) {
+ msg, ok := entry.Fields["MESSAGE"]
+ if !ok {
+ return "", fmt.Errorf("no MESSAGE field present in journal entry")
+ }
+
+ usec := entry.RealtimeTimestamp
+ timestamp := time.Unix(0, int64(usec)*int64(time.Microsecond))
+
+ return fmt.Sprintf("%s %s\n", timestamp, msg), nil
+}