initialise repo
[debian/orchestra.git] / go-patches / syslog-auto-reconnect.diff
1 diff -r b0819469a6df src/pkg/syslog/syslog.go
2 --- a/src/pkg/syslog/syslog.go  Thu Sep 08 12:16:42 2011 +1000
3 +++ b/src/pkg/syslog/syslog.go  Tue Sep 20 11:29:08 2011 +1000
4 @@ -17,23 +17,66 @@
5  type Priority int
6  
7  const (
8 -       // From /usr/include/sys/syslog.h.
9 -       // These are the same on Linux, BSD, and OS X.
10 -       LOG_EMERG Priority = iota
11 -       LOG_ALERT
12 -       LOG_CRIT
13 -       LOG_ERR
14 -       LOG_WARNING
15 -       LOG_NOTICE
16 -       LOG_INFO
17 -       LOG_DEBUG
18 +       // these are internal and are used to detect when overrides
19 +       // are in place.
20 +       SeverityPresent = Priority(0x100)
21 +       SeverityMask    = Priority(0x7)
22 +       FacilityPresent = Priority(0x200)
23 +       FacilityMask    = Priority(0x1f << 3)
24 +
25 +       // From the RFCs.  Names lifted from C.
26 +       LOG_EMERG   = Priority(0) | SeverityPresent
27 +       LOG_ALERT   = Priority(1) | SeverityPresent
28 +       LOG_CRIT    = Priority(2) | SeverityPresent
29 +       LOG_ERR     = Priority(3) | SeverityPresent
30 +       LOG_WARNING = Priority(4) | SeverityPresent
31 +       LOG_NOTICE  = Priority(5) | SeverityPresent
32 +       LOG_INFO    = Priority(6) | SeverityPresent
33 +       LOG_DEBUG   = Priority(7) | SeverityPresent
34 +
35 +       LOG_KERN     = Priority(0<<3) | FacilityPresent
36 +       LOG_USER     = Priority(1<<3) | FacilityPresent
37 +       LOG_MAIL     = Priority(2<<3) | FacilityPresent
38 +       LOG_DAEMON   = Priority(3<<3) | FacilityPresent
39 +       LOG_AUTH     = Priority(4<<3) | FacilityPresent
40 +       LOG_SYSLOG   = Priority(5<<3) | FacilityPresent
41 +       LOG_LPR      = Priority(6<<3) | FacilityPresent
42 +       LOG_NEWS     = Priority(7<<3) | FacilityPresent
43 +       LOG_UUCP     = Priority(8<<3) | FacilityPresent
44 +       LOG_CRON     = Priority(9<<3) | FacilityPresent
45 +       LOG_AUTHPRIV = Priority(10<<3) | FacilityPresent
46 +       LOG_FTP      = Priority(11<<3) | FacilityPresent
47 +       LOG_LOCAL0   = Priority(16<<3) | FacilityPresent
48 +       LOG_LOCAL1   = Priority(17<<3) | FacilityPresent
49 +       LOG_LOCAL2   = Priority(18<<3) | FacilityPresent
50 +       LOG_LOCAL3   = Priority(19<<3) | FacilityPresent
51 +       LOG_LOCAL4   = Priority(20<<3) | FacilityPresent
52 +       LOG_LOCAL5   = Priority(21<<3) | FacilityPresent
53 +       LOG_LOCAL6   = Priority(22<<3) | FacilityPresent
54 +       LOG_LOCAL7   = Priority(23<<3) | FacilityPresent
55  )
56  
57 +// splicePriority takes origPri, and mixes in a severity or facility
58 +// if present in mixPri.
59 +func splicePriority(origPri, mixPri Priority) (newPri Priority) {
60 +       newPri = origPri
61 +       if (mixPri & SeverityPresent) == SeverityPresent {
62 +               newPri = (newPri & ^SeverityMask) | (mixPri & SeverityMask)
63 +       }
64 +       if (mixPri & FacilityPresent) == FacilityPresent {
65 +               newPri = (newPri & ^FacilityMask) | (mixPri & FacilityMask)
66 +       }
67 +       return newPri
68 +}
69 +
70  // A Writer is a connection to a syslog server.
71  type Writer struct {
72         priority Priority
73         prefix   string
74         conn     serverConn
75 +       // persist the configured peer so we can reconnect when things really catch on fire.
76 +       network string
77 +       raddr   string
78  }
79  
80  type serverConn interface {
81 @@ -53,38 +96,104 @@
82         return Dial("", "", priority, prefix)
83  }
84  
85 -// Dial establishes a connection to a log daemon by connecting
86 -// to address raddr on the network net.
87 +// Dial sets up a connection to a log daemon.  The connection attempt
88 +// will be deferred until the first log message is sent.
89  // Each write to the returned writer sends a log message with
90  // the given priority and prefix.
91 +// If the prefix is empty, the binary's name - will be used in it's place.
92  func Dial(network, raddr string, priority Priority, prefix string) (w *Writer, err os.Error) {
93         if prefix == "" {
94                 prefix = os.Args[0]
95         }
96 -       var conn serverConn
97 -       if network == "" {
98 -               conn, err = unixSyslog()
99 +       return &Writer{priority & 0xFF, prefix, nil, network, raddr}, err
100 +}
101 +
102 +// Actually perform a real reconnect, closing any connections that may be open.
103 +func (w *Writer) Reconnect() (err os.Error) {
104 +       if w.conn != nil {
105 +               log.Printf("writer.Reconnect() on old connection.\n")
106 +               w.conn.close()
107 +               w.conn = nil
108 +       }
109 +       if w.network == "" {
110 +               w.conn, err = unixSyslog()
111         } else {
112                 var c net.Conn
113 -               c, err = net.Dial(network, raddr)
114 -               conn = netConn{c}
115 +               c, err = net.Dial(w.network, w.raddr)
116 +               w.conn = &netConn{c}
117         }
118 -       return &Writer{priority, prefix, conn}, err
119 +       return err
120 +}
121 +
122 +func canRetry(err os.Error) bool {
123 +       // not an error?  can't retry.
124 +       if err == nil {
125 +               return false
126 +       }
127 +       oe, ok := err.(*net.OpError)
128 +       if ok {
129 +               if oe.Error == os.ECONNREFUSED {
130 +                       return true
131 +               }
132 +       }
133 +       return false
134  }
135  
136  // Write sends a log message to the syslog daemon.
137 -func (w *Writer) Write(b []byte) (int, os.Error) {
138 -       if w.priority > LOG_DEBUG || w.priority < LOG_EMERG {
139 -               return 0, os.EINVAL
140 +func (w *Writer) Write(b []byte) (bout int, err os.Error) {
141 +       if w.conn == nil {
142 +               err = w.Reconnect()
143 +               if err != nil {
144 +                       return 0, err
145 +               }
146         }
147 -       return w.conn.writeBytes(w.priority, w.prefix, b)
148 +       retried := false
149 +retry:
150 +       bout, err = w.conn.writeBytes(w.priority, w.prefix, b)
151 +
152 +       if canRetry(err) && !retried {
153 +               retried = true
154 +               err = w.Reconnect()
155 +               if err != nil {
156 +                       return 0, err
157 +               }
158 +               goto retry
159 +       }
160 +       return bout, err
161  }
162  
163 -func (w *Writer) writeString(p Priority, s string) (int, os.Error) {
164 -       return w.conn.writeString(p, w.prefix, s)
165 +func (w *Writer) writeString(p Priority, s string) (bout int, err os.Error) {
166 +       msgpriority := splicePriority(w.priority, p)
167 +       if w.conn == nil {
168 +               err := w.Reconnect()
169 +               if err != nil {
170 +                       return 0, err
171 +               }
172 +       }
173 +       retried := false
174 +retry:
175 +       bout, err = w.conn.writeString(msgpriority, w.prefix, s)
176 +       if canRetry(err) && !retried {
177 +               log.Printf("Retrying: %s", err)
178 +               if err == os.ECONNREFUSED {
179 +                       log.Printf("Hit!")
180 +               }
181 +               retried = true
182 +               err = w.Reconnect()
183 +               if err != nil {
184 +                       return 0, err
185 +               }
186 +               goto retry
187 +       }
188 +       return bout, err
189  }
190  
191 -func (w *Writer) Close() os.Error { return w.conn.close() }
192 +func (w *Writer) Close() os.Error {
193 +       if w.conn != nil {
194 +               return w.conn.close()
195 +       }
196 +       return nil
197 +}
198  
199  // Emerg logs a message using the LOG_EMERG priority.
200  func (w *Writer) Emerg(m string) (err os.Error) {
201 @@ -124,15 +233,15 @@
202         return err
203  }
204  
205 -func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, os.Error) {
206 -       return fmt.Fprintf(n.conn, "<%d>%s: %s\n", p, prefix, b)
207 +func (n *netConn) writeBytes(p Priority, prefix string, b []byte) (int, os.Error) {
208 +       return fmt.Fprintf(n.conn, "<%d>%s: %s\n", p&0xFF, prefix, b)
209  }
210  
211 -func (n netConn) writeString(p Priority, prefix string, s string) (int, os.Error) {
212 -       return fmt.Fprintf(n.conn, "<%d>%s: %s\n", p, prefix, s)
213 +func (n *netConn) writeString(p Priority, prefix string, s string) (int, os.Error) {
214 +       return fmt.Fprintf(n.conn, "<%d>%s: %s\n", p&0xFF, prefix, s)
215  }
216  
217 -func (n netConn) close() os.Error {
218 +func (n *netConn) close() os.Error {
219         return n.conn.Close()
220  }
221  
222 diff -r b0819469a6df src/pkg/syslog/syslog_test.go
223 --- a/src/pkg/syslog/syslog_test.go     Thu Sep 08 12:16:42 2011 +1000
224 +++ b/src/pkg/syslog/syslog_test.go     Tue Sep 20 11:29:08 2011 +1000
225 @@ -7,11 +7,14 @@
226         "io"
227         "log"
228         "net"
229 +       "os"
230         "testing"
231  )
232  
233  var serverAddr string
234  
235 +const testSocketAddress = "/tmp/fakelog"
236 +
237  func runSyslog(c net.PacketConn, done chan<- string) {
238         var buf [4096]byte
239         var rcvd string = ""
240 @@ -22,10 +25,12 @@
241                 }
242                 rcvd += string(buf[0:n])
243         }
244 +
245 +       c.Close()
246         done <- rcvd
247  }
248  
249 -func startServer(done chan<- string) {
250 +func startUDPServer(done chan<- string) {
251         c, e := net.ListenPacket("udp", "127.0.0.1:0")
252         if e != nil {
253                 log.Fatalf("net.ListenPacket failed udp :0 %v", e)
254 @@ -35,6 +40,39 @@
255         go runSyslog(c, done)
256  }
257  
258 +func runUDPSyslog(c *net.UDPConn, done chan<- string) {
259 +       var buf [4096]byte
260 +       var rcvd string = ""
261 +       for {
262 +               n, err := c.Read(buf[0:])
263 +               if err != nil || n == 0 {
264 +                       break
265 +               }
266 +               rcvd += string(buf[0:n])
267 +       }
268 +
269 +       c.Close()
270 +       done <- rcvd
271 +}
272 +
273 +func startUnixServer(done chan<- string) (ready <-chan int) {
274 +       syslogready := make(chan int, 1)
275 +       os.Remove(testSocketAddress)
276 +       uaddr, e := net.ResolveUnixAddr("unixgram", testSocketAddress)
277 +       if e != nil {
278 +               log.Fatalf("net.ResolveUnixAddr failed: %v", e)
279 +       }
280 +       c, e := net.ListenUnixgram("unixgram", uaddr)
281 +       if e != nil {
282 +               log.Fatalf("net.ListenPacket failed unixgram /tmp/fakelog %v", e)
283 +       }
284 +
285 +       c.SetReadTimeout(1000e6) // 100ms
286 +       go runUDPSyslog(c, done)
287 +       syslogready <- 1
288 +       return syslogready
289 +}
290 +
291  func skipNetTest(t *testing.T) bool {
292         if testing.Short() {
293                 // Depends on syslog daemon running, and sometimes it's not.
294 @@ -44,6 +82,57 @@
295         return false
296  }
297  
298 +func TestFakeUnixSyslog(t *testing.T) {
299 +       done := make(chan string)
300 +       startUnixServer(done)
301 +       s, err := Dial("unixgram", "/tmp/fakelog", LOG_INFO|LOG_LOCAL0, "syslog_test")
302 +       if err != nil {
303 +               t.Fatalf("Dial() failed: %s", err)
304 +       }
305 +       err = s.Debug("Moo")
306 +       if err != nil {
307 +               t.Fatalf("s.Debug() failed: %s", err)
308 +       }
309 +       expected := "<135>syslog_test: Moo\n"
310 +       rcvd := <-done
311 +       if rcvd != expected {
312 +               t.Fatalf("s.Debug() = '%q', but wanted '%q'", rcvd, expected)
313 +       }
314 +       s.Close()
315 +}
316 +
317 +func TestFlap(t *testing.T) {
318 +       done := make(chan string)
319 +       startUnixServer(done)
320 +       s, err := Dial("unixgram", "/tmp/fakelog", LOG_INFO|LOG_LOCAL0, "syslog_test")
321 +       if err != nil {
322 +               t.Fatalf("Dial() failed: %s", err)
323 +       }
324 +       err = s.Debug("Moo")
325 +       if err != nil {
326 +               t.Fatalf("s.Debug() failed: %s", err)
327 +       }
328 +       expected := "<135>syslog_test: Moo\n"
329 +       rcvd := <-done
330 +       if rcvd != expected {
331 +               t.Fatalf("s.Debug() = '%q', but wanted '%q'", rcvd, expected)
332 +       }
333 +       // restart the server.
334 +       <-startUnixServer(done)
335 +
336 +       // and try retransmitting.
337 +       err = s.Debug("Re-Moo")
338 +       if err != nil {
339 +               t.Fatalf("s.Debug() failed: %s", err)
340 +       }
341 +       expected = "<135>syslog_test: Re-Moo\n"
342 +       rcvd = <-done
343 +       if rcvd != expected {
344 +               t.Fatalf("s.Info() = '%q', but wanted '%q'", rcvd, expected)
345 +       }
346 +       s.Close()
347 +}
348 +
349  func TestNew(t *testing.T) {
350         if skipNetTest(t) {
351                 return
352 @@ -79,8 +168,10 @@
353  
354  func TestUDPDial(t *testing.T) {
355         done := make(chan string)
356 -       startServer(done)
357 -       l, err := Dial("udp", serverAddr, LOG_INFO, "syslog_test")
358 +       startUDPServer(done)
359 +       // it's important that the Dial priority is different to the
360 +       // actual message sent - this tests priority splicing.
361 +       l, err := Dial("udp", serverAddr, LOG_DEBUG, "syslog_test")
362         if err != nil {
363                 t.Fatalf("syslog.Dial() failed: %s", err)
364         }
365 @@ -95,7 +186,7 @@
366  
367  func TestWrite(t *testing.T) {
368         done := make(chan string)
369 -       startServer(done)
370 +       startUDPServer(done)
371         l, err := Dial("udp", serverAddr, LOG_ERR, "syslog_test")
372         if err != nil {
373                 t.Fatalf("syslog.Dial() failed: %s", err)
374 diff -r b0819469a6df src/pkg/syslog/syslog_unix.go
375 --- a/src/pkg/syslog/syslog_unix.go     Thu Sep 08 12:16:42 2011 +1000
376 +++ b/src/pkg/syslog/syslog_unix.go     Tue Sep 20 11:29:08 2011 +1000
377 @@ -23,7 +23,7 @@
378                         if err != nil {
379                                 continue
380                         } else {
381 -                               return netConn{conn}, nil
382 +                               return &netConn{conn}, nil
383                         }
384                 }
385         }