2323package journal
2424
2525import (
26- "bytes"
27- "encoding/binary"
28- "errors"
2926 "fmt"
30- "io"
31- "io/ioutil"
32- "net"
33- "os"
34- "strconv"
35- "strings"
36- "sync"
37- "sync/atomic"
38- "syscall"
39- "unsafe"
4027)
4128
4229// Priority of a journal message
@@ -53,173 +40,7 @@ const (
5340 PriDebug
5441)
5542
56- var (
57- // This can be overridden at build-time:
58- // https://github.com/golang/go/wiki/GcToolchainTricks#including-build-information-in-the-executable
59- journalSocket = "/run/systemd/journal/socket"
60-
61- // unixConnPtr atomically holds the local unconnected Unix-domain socket.
62- // Concrete safe pointer type: *net.UnixConn
63- unixConnPtr unsafe.Pointer
64- // onceConn ensures that unixConnPtr is initialized exactly once.
65- onceConn sync.Once
66- )
67-
68- func init () {
69- onceConn .Do (initConn )
70- }
71-
72- // Enabled checks whether the local systemd journal is available for logging.
73- func Enabled () bool {
74- onceConn .Do (initConn )
75-
76- if (* net .UnixConn )(atomic .LoadPointer (& unixConnPtr )) == nil {
77- return false
78- }
79-
80- if _ , err := net .Dial ("unixgram" , journalSocket ); err != nil {
81- return false
82- }
83-
84- return true
85- }
86-
87- // Send a message to the local systemd journal. vars is a map of journald
88- // fields to values. Fields must be composed of uppercase letters, numbers,
89- // and underscores, but must not start with an underscore. Within these
90- // restrictions, any arbitrary field name may be used. Some names have special
91- // significance: see the journalctl documentation
92- // (http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html)
93- // for more details. vars may be nil.
94- func Send (message string , priority Priority , vars map [string ]string ) error {
95- conn := (* net .UnixConn )(atomic .LoadPointer (& unixConnPtr ))
96- if conn == nil {
97- return errors .New ("could not initialize socket to journald" )
98- }
99-
100- socketAddr := & net.UnixAddr {
101- Name : journalSocket ,
102- Net : "unixgram" ,
103- }
104-
105- data := new (bytes.Buffer )
106- appendVariable (data , "PRIORITY" , strconv .Itoa (int (priority )))
107- appendVariable (data , "MESSAGE" , message )
108- for k , v := range vars {
109- appendVariable (data , k , v )
110- }
111-
112- _ , _ , err := conn .WriteMsgUnix (data .Bytes (), nil , socketAddr )
113- if err == nil {
114- return nil
115- }
116- if ! isSocketSpaceError (err ) {
117- return err
118- }
119-
120- // Large log entry, send it via tempfile and ancillary-fd.
121- file , err := tempFd ()
122- if err != nil {
123- return err
124- }
125- defer file .Close ()
126- _ , err = io .Copy (file , data )
127- if err != nil {
128- return err
129- }
130- rights := syscall .UnixRights (int (file .Fd ()))
131- _ , _ , err = conn .WriteMsgUnix ([]byte {}, rights , socketAddr )
132- if err != nil {
133- return err
134- }
135-
136- return nil
137- }
138-
13943// Print prints a message to the local systemd journal using Send().
14044func Print (priority Priority , format string , a ... interface {}) error {
14145 return Send (fmt .Sprintf (format , a ... ), priority , nil )
14246}
143-
144- func appendVariable (w io.Writer , name , value string ) {
145- if err := validVarName (name ); err != nil {
146- fmt .Fprintf (os .Stderr , "variable name %s contains invalid character, ignoring\n " , name )
147- }
148- if strings .ContainsRune (value , '\n' ) {
149- /* When the value contains a newline, we write:
150- * - the variable name, followed by a newline
151- * - the size (in 64bit little endian format)
152- * - the data, followed by a newline
153- */
154- fmt .Fprintln (w , name )
155- binary .Write (w , binary .LittleEndian , uint64 (len (value )))
156- fmt .Fprintln (w , value )
157- } else {
158- /* just write the variable and value all on one line */
159- fmt .Fprintf (w , "%s=%s\n " , name , value )
160- }
161- }
162-
163- // validVarName validates a variable name to make sure journald will accept it.
164- // The variable name must be in uppercase and consist only of characters,
165- // numbers and underscores, and may not begin with an underscore:
166- // https://www.freedesktop.org/software/systemd/man/sd_journal_print.html
167- func validVarName (name string ) error {
168- if name == "" {
169- return errors .New ("Empty variable name" )
170- } else if name [0 ] == '_' {
171- return errors .New ("Variable name begins with an underscore" )
172- }
173-
174- for _ , c := range name {
175- if ! (('A' <= c && c <= 'Z' ) || ('0' <= c && c <= '9' ) || c == '_' ) {
176- return errors .New ("Variable name contains invalid characters" )
177- }
178- }
179- return nil
180- }
181-
182- // isSocketSpaceError checks whether the error is signaling
183- // an "overlarge message" condition.
184- func isSocketSpaceError (err error ) bool {
185- opErr , ok := err .(* net.OpError )
186- if ! ok || opErr == nil {
187- return false
188- }
189-
190- sysErr , ok := opErr .Err .(* os.SyscallError )
191- if ! ok || sysErr == nil {
192- return false
193- }
194-
195- return sysErr .Err == syscall .EMSGSIZE || sysErr .Err == syscall .ENOBUFS
196- }
197-
198- // tempFd creates a temporary, unlinked file under `/dev/shm`.
199- func tempFd () (* os.File , error ) {
200- file , err := ioutil .TempFile ("/dev/shm/" , "journal.XXXXX" )
201- if err != nil {
202- return nil , err
203- }
204- err = syscall .Unlink (file .Name ())
205- if err != nil {
206- return nil , err
207- }
208- return file , nil
209- }
210-
211- // initConn initializes the global `unixConnPtr` socket.
212- // It is meant to be called exactly once, at program startup.
213- func initConn () {
214- autobind , err := net .ResolveUnixAddr ("unixgram" , "" )
215- if err != nil {
216- return
217- }
218-
219- sock , err := net .ListenUnixgram ("unixgram" , autobind )
220- if err != nil {
221- return
222- }
223-
224- atomic .StorePointer (& unixConnPtr , unsafe .Pointer (sock ))
225- }
0 commit comments