23
23
package journal
24
24
25
25
import (
26
- "bytes"
27
- "encoding/binary"
28
- "errors"
29
26
"fmt"
30
- "io"
31
- "io/ioutil"
32
- "net"
33
- "os"
34
- "strconv"
35
- "strings"
36
- "sync"
37
- "sync/atomic"
38
- "syscall"
39
- "unsafe"
40
27
)
41
28
42
29
// Priority of a journal message
@@ -53,173 +40,7 @@ const (
53
40
PriDebug
54
41
)
55
42
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
-
139
43
// Print prints a message to the local systemd journal using Send().
140
44
func Print (priority Priority , format string , a ... interface {}) error {
141
45
return Send (fmt .Sprintf (format , a ... ), priority , nil )
142
46
}
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