summaryrefslogtreecommitdiffstats
path: root/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/send.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 12:36:04 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 12:36:04 +0000
commitb09c6d56832eb1718c07d74abf3bc6ae3fe4e030 (patch)
treed2caec2610d4ea887803ec9e9c3cd77136c448ba /dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/send.go
parentInitial commit. (diff)
downloadicingadb-upstream.tar.xz
icingadb-upstream.zip
Adding upstream version 1.1.0.upstream/1.1.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/send.go')
-rw-r--r--dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/send.go275
1 files changed, 275 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/send.go b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/send.go
new file mode 100644
index 0000000..19948cf
--- /dev/null
+++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/send.go
@@ -0,0 +1,275 @@
+// Copyright 2017 Grigory Zubankov. All rights reserved.
+// Use of this source code is governed by a MIT license
+// that can be found in the LICENSE file.
+//
+
+package journald
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "os"
+ "strconv"
+ "sync"
+ "syscall"
+)
+
+// Priority is the numeric message priority value as known from BSD syslog
+type Priority int
+
+// Priority values
+const (
+ PriorityEmerg Priority = iota
+ PriorityAlert
+ PriorityCrit
+ PriorityErr
+ PriorityWarning
+ PriorityNotice
+ PriorityInfo
+ PriorityDebug
+)
+
+var (
+ addr = &net.UnixAddr{Name: "/run/systemd/journal/socket", Net: "unixgram"}
+
+ // DefaultJournal is the default journal and is used by Print and Send.
+ DefaultJournal = &Journal{}
+)
+
+// IsNotExist checks if the system journal is not exist.
+func IsNotExist() bool {
+ _, err := os.Stat(addr.Name)
+ return os.IsNotExist(err)
+}
+
+// Journal keeps a connection to the system journal
+type Journal struct {
+ once sync.Once
+ conn *net.UnixConn
+ connErr error
+
+ // NormalizeFieldNameFn is a hook that allows client to change
+ // fields names just before sending if set.
+ //
+ // Default value is nil.
+ // string.ToUpper is a good example of the hook usage.
+ NormalizeFieldNameFn func(string) string
+
+ // TestModeEnabled allows Journal to do nothing. All messages are
+ // discarding.
+ TestModeEnabled bool
+}
+
+// Print may be used to submit simple, plain text log entries to the
+// system journal. The first argument is a priority value. This is
+// followed by a format string and its parameters.
+func (j *Journal) Print(p Priority, format string, a ...interface{}) error {
+ return j.Send(fmt.Sprintf(format, a...), p, nil)
+}
+
+// WriteMsg writes the given bytes to the systemd journal's socket.
+// The caller is in charge of correct data format.
+func (j *Journal) WriteMsg(data []byte) error {
+ if j.TestModeEnabled {
+ return nil
+ }
+
+ c, err := j.journalConn()
+ if err != nil {
+ return err
+ }
+
+ _, _, err = c.WriteMsgUnix(data, nil, addr)
+ if err == nil {
+ return nil
+ }
+ errno := toErrno(err)
+ if errno != syscall.EMSGSIZE && errno != syscall.ENOBUFS {
+ return err
+ }
+
+ // Message doesn't fit...
+
+ // TODO: user memfd when it will be available in go:
+ // http://man7.org/linux/man-pages/man2/memfd_create.2.html
+
+ f, err := openTempFileUnlinkable()
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ _, err = f.Write(data)
+ if err != nil {
+ return err
+ }
+ _, err = writeMsgUnix(c, syscall.UnixRights(int(f.Fd())), addr)
+
+ return err
+
+}
+
+// Send may be used to submit structured log entries to the system
+// journal. It takes a map of fields with names and values.
+//
+// The field names must be in uppercase and consist only of characters,
+// numbers and underscores, and may not begin with an underscore. All
+// fields that do not follow this syntax will be ignored. The value can
+// be of any size and format. A variable may be assigned more than one
+// value per entry.
+//
+// A number of well known fields are defined, see:
+// http://0pointer.de/public/systemd-man/systemd.journal-fields.html
+//
+func (j *Journal) Send(msg string, p Priority, fields map[string]interface{}) error {
+ return j.WriteMsg(j.marshal(msg, p, fields))
+}
+
+// Close closes the underlying connection.
+func (j *Journal) Close() error {
+ if j.connErr != nil {
+ return j.connErr
+ }
+ if j.conn == nil {
+ return nil
+ }
+
+ return j.conn.Close()
+}
+
+func (j *Journal) journalConn() (*net.UnixConn, error) {
+ j.once.Do(func() {
+ // File from UNIX socket. This is the only way to create a
+ // connect-less socket. Both Dial and ListenUnixgram do extra
+ // work it terms of connection.
+ //
+ // This UNIX socket is needed only to make a Conn on it's base.
+ // No reason to switch it to nonblocking mode and set additional
+ // flags. FileConn will do such things for us for Conn socket.
+ fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0)
+ if err != nil {
+ j.connErr = err
+ return
+ }
+ f := os.NewFile(uintptr(fd), "base UNIX socket")
+ defer f.Close()
+
+ fc, err := net.FileConn(f)
+ if err != nil {
+ j.connErr = err
+ return
+ }
+ uc, ok := fc.(*net.UnixConn)
+ if !ok {
+ fc.Close()
+ j.connErr = errors.New("not a UNIX connection")
+ return
+ }
+ uc.SetWriteBuffer(8 * 1024 * 1024)
+ j.conn = uc
+ })
+
+ return j.conn, j.connErr
+}
+
+// Print may be used to submit simple, plain text log entries to the
+// system journal. The first argument is a priority value. This is
+// followed by a format string and its parameters.
+//
+// Print is a wrapper around DefaultJournal.Print.
+func Print(p Priority, format string, a ...interface{}) error {
+ return DefaultJournal.Print(p, format, a...)
+}
+
+// Send may be used to submit structured log entries to the system
+// journal. It takes a map of fields with names and values.
+//
+// The field names must be in uppercase and consist only of characters,
+// numbers and underscores, and may not begin with an underscore. All
+// fields that do not follow this syntax will be ignored. The value can
+// be of any size and format. A variable may be assigned more than one
+// value per entry.
+//
+// A number of well known fields are defined, see:
+// http://0pointer.de/public/systemd-man/systemd.journal-fields.html
+//
+// Send is a wrapper around DefaultJournal.Send.
+func Send(msg string, p Priority, fields map[string]interface{}) error {
+ return DefaultJournal.Send(msg, p, fields)
+}
+
+func toErrno(err error) syscall.Errno {
+ switch e := err.(type) {
+ case *net.OpError:
+ switch e := e.Err.(type) {
+ case *os.SyscallError:
+ return e.Err.(syscall.Errno)
+ }
+ }
+
+ return 0
+}
+
+func (j *Journal) marshal(msg string, p Priority, fields map[string]interface{}) []byte {
+ bb := new(bytes.Buffer)
+ j.writeField(bb, "PRIORITY", strconv.Itoa(int(p)))
+ j.writeField(bb, "MESSAGE", msg)
+ for k, v := range fields {
+ j.writeField(bb, k, v)
+ }
+
+ return bb.Bytes()
+}
+
+func (j *Journal) writeField(w io.Writer, name string, value interface{}) {
+ w.Write([]byte(j.normalizeFieldName(name)))
+ dv := valueToBytes(value)
+ if bytes.ContainsRune(dv, '\n') {
+ // According to the format, if the value includes a newline
+ // need to write the field name, plus a newline, then the
+ // size (64bit LE), the field data and a final newline.
+
+ w.Write([]byte{'\n'})
+ binary.Write(w, binary.LittleEndian, uint64(len(dv)))
+ } else {
+ w.Write([]byte{'='})
+ }
+ w.Write(dv)
+ w.Write([]byte{'\n'})
+}
+
+func (j *Journal) normalizeFieldName(s string) string {
+ if j.NormalizeFieldNameFn != nil {
+ return j.NormalizeFieldNameFn(s)
+ }
+ return s
+}
+
+func valueToBytes(value interface{}) []byte {
+ switch rv := value.(type) {
+ case string:
+ return []byte(rv)
+ case []byte:
+ return rv
+ default:
+ return []byte(fmt.Sprint(value))
+ }
+}
+
+func openTempFileUnlinkable() (*os.File, error) {
+ f, err := ioutil.TempFile("/dev/shm/", "journald-send-tmp")
+ if err != nil {
+ return nil, err
+ }
+ // The file will be deleted just after the systemd will close passed fd.
+ err = syscall.Unlink(f.Name())
+ if err != nil {
+ return nil, err
+ }
+
+ return f, nil
+}