summaryrefslogtreecommitdiff
path: root/vendor/github.com
diff options
context:
space:
mode:
authorbaude <bbaude@redhat.com>2019-03-27 13:50:54 -0500
committerbaude <bbaude@redhat.com>2019-04-24 16:00:04 -0500
commit7bf7c177ab3f67d5de1689842204c258fca083e4 (patch)
tree960fd1365913209fec61e3226b96a6f8bb1fd051 /vendor/github.com
parentd75543fcd2ce87a9b87b8883400f355979004e91 (diff)
downloadpodman-7bf7c177ab3f67d5de1689842204c258fca083e4.tar.gz
podman-7bf7c177ab3f67d5de1689842204c258fca083e4.tar.bz2
podman-7bf7c177ab3f67d5de1689842204c258fca083e4.zip
journald event logging
add the ability for podman to read and write events to journald instead of just a logfile. This can be controlled in libpod.conf with the `events_logger` attribute of `journald` or `file`. The default will be set to `journald`. Signed-off-by: baude <bbaude@redhat.com>
Diffstat (limited to 'vendor/github.com')
-rw-r--r--vendor/github.com/coreos/go-systemd/journal/journal.go179
-rw-r--r--vendor/github.com/coreos/go-systemd/sdjournal/functions.go66
-rw-r--r--vendor/github.com/coreos/go-systemd/sdjournal/journal.go1024
-rw-r--r--vendor/github.com/coreos/go-systemd/sdjournal/read.go260
-rw-r--r--vendor/github.com/coreos/pkg/LICENSE202
-rw-r--r--vendor/github.com/coreos/pkg/NOTICE5
-rw-r--r--vendor/github.com/coreos/pkg/README.md4
-rw-r--r--vendor/github.com/coreos/pkg/dlopen/dlopen.go82
-rw-r--r--vendor/github.com/coreos/pkg/dlopen/dlopen_example.go56
9 files changed, 1878 insertions, 0 deletions
diff --git a/vendor/github.com/coreos/go-systemd/journal/journal.go b/vendor/github.com/coreos/go-systemd/journal/journal.go
new file mode 100644
index 000000000..7f434990d
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/journal/journal.go
@@ -0,0 +1,179 @@
+// 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"
+ "syscall"
+)
+
+// Priority of a journal message
+type Priority int
+
+const (
+ PriEmerg Priority = iota
+ PriAlert
+ PriCrit
+ PriErr
+ PriWarning
+ PriNotice
+ PriInfo
+ PriDebug
+)
+
+var conn net.Conn
+
+func init() {
+ var err error
+ conn, err = net.Dial("unixgram", "/run/systemd/journal/socket")
+ if err != nil {
+ conn = nil
+ }
+}
+
+// Enabled returns true if the local systemd journal is available for logging
+func Enabled() bool {
+ return conn != nil
+}
+
+// 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 {
+ if conn == nil {
+ return journalError("could not connect to journald socket")
+ }
+
+ 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 := io.Copy(conn, data)
+ if err != nil && isSocketSpaceError(err) {
+ file, err := tempFd()
+ if err != nil {
+ return journalError(err.Error())
+ }
+ defer file.Close()
+ _, err = io.Copy(file, data)
+ if err != nil {
+ return journalError(err.Error())
+ }
+
+ rights := syscall.UnixRights(int(file.Fd()))
+
+ /* this connection should always be a UnixConn, but better safe than sorry */
+ unixConn, ok := conn.(*net.UnixConn)
+ if !ok {
+ return journalError("can't send file through non-Unix connection")
+ }
+ unixConn.WriteMsgUnix([]byte{}, rights, nil)
+ } else if err != nil {
+ return journalError(err.Error())
+ }
+ 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 !validVarName(name) {
+ journalError("variable name contains invalid character, ignoring")
+ }
+ 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)
+ }
+}
+
+func validVarName(name string) bool {
+ /* The variable name must be in uppercase and consist only of characters,
+ * numbers and underscores, and may not begin with an underscore. (from the docs)
+ */
+
+ valid := name[0] != '_'
+ for _, c := range name {
+ valid = valid && ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_'
+ }
+ return valid
+}
+
+func isSocketSpaceError(err error) bool {
+ opErr, ok := err.(*net.OpError)
+ if !ok {
+ return false
+ }
+
+ sysErr, ok := opErr.Err.(syscall.Errno)
+ if !ok {
+ return false
+ }
+
+ return sysErr == syscall.EMSGSIZE || sysErr == syscall.ENOBUFS
+}
+
+func tempFd() (*os.File, error) {
+ file, err := ioutil.TempFile("/dev/shm/", "journal.XXXXX")
+ if err != nil {
+ return nil, err
+ }
+ syscall.Unlink(file.Name())
+ if err != nil {
+ return nil, err
+ }
+ return file, nil
+}
+
+func journalError(s string) error {
+ s = "journal error: " + s
+ fmt.Fprintln(os.Stderr, s)
+ return errors.New(s)
+}
diff --git a/vendor/github.com/coreos/go-systemd/sdjournal/functions.go b/vendor/github.com/coreos/go-systemd/sdjournal/functions.go
new file mode 100644
index 000000000..e132369c1
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/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/pkg/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/sdjournal/journal.go b/vendor/github.com/coreos/go-systemd/sdjournal/journal.go
new file mode 100644
index 000000000..b00d606c1
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/sdjournal/journal.go
@@ -0,0 +1,1024 @@
+// 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);
+// }
+//
+// 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);
+// }
+//
+import "C"
+import (
+ "bytes"
+ "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
+)
+
+// 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: %d", syscall.Errno(-r))
+ }
+
+ return j, nil
+}
+
+// NewJournalFromDir returns a new Journal instance pointing to a journal residing
+// in a given directory. The supplied path may be relative or absolute; if
+// relative, it will be converted to an absolute path before being opened.
+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: %d", path, syscall.Errno(-r))
+ }
+
+ 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: %d", syscall.Errno(-r))
+ }
+
+ 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: %d", syscall.Errno(-r))
+ }
+
+ 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: %d", syscall.Errno(-r))
+ }
+
+ 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: %d", syscall.Errno(-r))
+ }
+
+ 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: %d", syscall.Errno(-r))
+ }
+
+ 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: %d", syscall.Errno(-r))
+ }
+
+ 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: %d", syscall.Errno(-r))
+ }
+
+ 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: %d", syscall.Errno(-r))
+ }
+
+ return d, C.int(l), nil
+}
+
+// GetData gets the data object associated with a specific field from the
+// current journal entry.
+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
+// current journal entry, returning only the value of the object.
+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
+// current journal entry.
+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
+// current journal entry, returning only the value of the object.
+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 a journal entry with
+// all key-value pairs of data as well as address fields (cursor, realtime
+// timestamp and monotonic timestamp)
+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: %d", syscall.Errno(-r))
+ }
+
+ 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: %d", syscall.Errno(-r))
+ }
+
+ 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: %d", syscall.Errno(-r))
+ }
+
+ 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: %d", syscall.Errno(-r))
+ }
+
+ 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
+}
+
+// SetDataThresold 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: %d", syscall.Errno(-r))
+ }
+
+ return nil
+}
+
+// GetRealtimeUsec gets the realtime (wallclock) timestamp of the current
+// journal entry.
+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: %d", syscall.Errno(-r))
+ }
+
+ return uint64(usec), nil
+}
+
+// GetMonotonicUsec gets the monotonic timestamp of the current journal entry.
+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: %d", syscall.Errno(-r))
+ }
+
+ return uint64(usec), nil
+}
+
+// GetCursor gets the cursor of the current journal entry.
+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: %d", syscall.Errno(-r))
+ }
+
+ 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: %d", cursor, syscall.Errno(-r))
+ }
+
+ return nil
+}
+
+// SeekHead seeks to the beginning of the journal, i.e. the oldest available
+// entry.
+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: %d", syscall.Errno(-r))
+ }
+
+ return nil
+}
+
+// SeekTail may be used to seek to the end of the journal, i.e. the most recent
+// available entry.
+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: %d", syscall.Errno(-r))
+ }
+
+ return nil
+}
+
+// SeekRealtimeUsec seeks to the entry with the specified realtime (wallclock)
+// timestamp, i.e. CLOCK_REALTIME.
+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: %d", usec, syscall.Errno(-r))
+ }
+
+ return nil
+}
+
+// SeekCursor seeks to a concrete journal cursor.
+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: %d", cursor, syscall.Errno(-r))
+ }
+
+ 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(time.Now().Add(timeout).Unix() / 1000)
+ }
+ 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: %d", syscall.Errno(-r))
+ }
+
+ 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: %d", syscall.Errno(-r))
+ }
+
+ // 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: %d", syscall.Errno(-r))
+ }
+
+ 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
+}
diff --git a/vendor/github.com/coreos/go-systemd/sdjournal/read.go b/vendor/github.com/coreos/go-systemd/sdjournal/read.go
new file mode 100644
index 000000000..b581f03b4
--- /dev/null
+++ b/vendor/github.com/coreos/go-systemd/sdjournal/read.go
@@ -0,0 +1,260 @@
+// 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"
+ "time"
+)
+
+var (
+ 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
+}
+
+// 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
+}
+
+// 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) {
+ r := &JournalReader{}
+
+ // 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 {
+ r.journal.AddMatch(m.String())
+ }
+
+ // 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) {
+ var err error
+
+ if r.msgReader == nil {
+ var c uint64
+
+ // 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
+ }
+
+ // Build a message
+ var msg string
+ msg, err = r.buildMessage()
+
+ if err != nil {
+ return 0, err
+ }
+ r.msgReader = strings.NewReader(msg)
+ }
+
+ // Copy and return the message
+ var sz int
+ 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) (err 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))
+process:
+ for {
+ c, err := r.Read(msg)
+ if err != nil && err != io.EOF {
+ break process
+ }
+
+ select {
+ case <-until:
+ return ErrExpired
+ default:
+ if c > 0 {
+ if _, err = writer.Write(msg[:c]); err != nil {
+ break process
+ }
+ 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.
+ events := make(chan int, 1)
+ pollDone := make(chan bool, 1)
+ go func() {
+ for {
+ select {
+ case <-pollDone:
+ return
+ default:
+ events <- r.journal.Wait(time.Duration(1) * time.Second)
+ }
+ }
+ }()
+
+ select {
+ case <-until:
+ pollDone <- true
+ return ErrExpired
+ case e := <-events:
+ pollDone <- true
+ switch e {
+ case SD_JOURNAL_NOP, SD_JOURNAL_APPEND, SD_JOURNAL_INVALIDATE:
+ // TODO: need to account for any of these?
+ default:
+ log.Printf("Received unknown event: %d\n", e)
+ }
+ continue process
+ }
+ }
+
+ return
+}
+
+// buildMessage returns a string representing the current journal entry in a simple format which
+// includes the entry timestamp and MESSAGE field.
+func (r *JournalReader) buildMessage() (string, error) {
+ var msg string
+ var usec uint64
+ var err error
+
+ if msg, err = r.journal.GetData("MESSAGE"); err != nil {
+ return "", err
+ }
+
+ if usec, err = r.journal.GetRealtimeUsec(); err != nil {
+ return "", err
+ }
+
+ timestamp := time.Unix(0, int64(usec)*int64(time.Microsecond))
+
+ return fmt.Sprintf("%s %s\n", timestamp, msg), nil
+}
diff --git a/vendor/github.com/coreos/pkg/LICENSE b/vendor/github.com/coreos/pkg/LICENSE
new file mode 100644
index 000000000..e06d20818
--- /dev/null
+++ b/vendor/github.com/coreos/pkg/LICENSE
@@ -0,0 +1,202 @@
+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:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) 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
+
+ (d) 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/pkg/NOTICE b/vendor/github.com/coreos/pkg/NOTICE
new file mode 100644
index 000000000..b39ddfa5c
--- /dev/null
+++ b/vendor/github.com/coreos/pkg/NOTICE
@@ -0,0 +1,5 @@
+CoreOS Project
+Copyright 2014 CoreOS, Inc
+
+This product includes software developed at CoreOS, Inc.
+(http://www.coreos.com/).
diff --git a/vendor/github.com/coreos/pkg/README.md b/vendor/github.com/coreos/pkg/README.md
new file mode 100644
index 000000000..ca68a07f0
--- /dev/null
+++ b/vendor/github.com/coreos/pkg/README.md
@@ -0,0 +1,4 @@
+a collection of go utility packages
+
+[![Build Status](https://travis-ci.org/coreos/pkg.png?branch=master)](https://travis-ci.org/coreos/pkg)
+[![Godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/coreos/pkg)
diff --git a/vendor/github.com/coreos/pkg/dlopen/dlopen.go b/vendor/github.com/coreos/pkg/dlopen/dlopen.go
new file mode 100644
index 000000000..23774f612
--- /dev/null
+++ b/vendor/github.com/coreos/pkg/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/pkg/dlopen/dlopen_example.go b/vendor/github.com/coreos/pkg/dlopen/dlopen_example.go
new file mode 100644
index 000000000..48a660104
--- /dev/null
+++ b/vendor/github.com/coreos/pkg/dlopen/dlopen_example.go
@@ -0,0 +1,56 @@
+// 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.
+//
+// +build linux
+
+package dlopen
+
+// #include <string.h>
+// #include <stdlib.h>
+//
+// int
+// my_strlen(void *f, const char *s)
+// {
+// size_t (*strlen)(const char *);
+//
+// strlen = (size_t (*)(const char *))f;
+// return strlen(s);
+// }
+import "C"
+
+import (
+ "fmt"
+ "unsafe"
+)
+
+func strlen(libs []string, s string) (int, error) {
+ h, err := GetHandle(libs)
+ if err != nil {
+ return -1, fmt.Errorf(`couldn't get a handle to the library: %v`, err)
+ }
+ defer h.Close()
+
+ f := "strlen"
+ cs := C.CString(s)
+ defer C.free(unsafe.Pointer(cs))
+
+ strlen, err := h.GetSymbolPointer(f)
+ if err != nil {
+ return -1, fmt.Errorf(`couldn't get symbol %q: %v`, f, err)
+ }
+
+ len := C.my_strlen(strlen, cs)
+
+ return int(len), nil
+}