Home | History | Annotate | Download | only in syslog
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // +build !windows,!nacl,!plan9
      6 
      7 package syslog
      8 
      9 import (
     10 	"bufio"
     11 	"fmt"
     12 	"io"
     13 	"io/ioutil"
     14 	"log"
     15 	"net"
     16 	"os"
     17 	"runtime"
     18 	"sync"
     19 	"testing"
     20 	"time"
     21 )
     22 
     23 func runPktSyslog(c net.PacketConn, done chan<- string) {
     24 	var buf [4096]byte
     25 	var rcvd string
     26 	ct := 0
     27 	for {
     28 		var n int
     29 		var err error
     30 
     31 		c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
     32 		n, _, err = c.ReadFrom(buf[:])
     33 		rcvd += string(buf[:n])
     34 		if err != nil {
     35 			if oe, ok := err.(*net.OpError); ok {
     36 				if ct < 3 && oe.Temporary() {
     37 					ct++
     38 					continue
     39 				}
     40 			}
     41 			break
     42 		}
     43 	}
     44 	c.Close()
     45 	done <- rcvd
     46 }
     47 
     48 var crashy = false
     49 
     50 func testableNetwork(network string) bool {
     51 	switch network {
     52 	case "unix", "unixgram":
     53 		switch runtime.GOOS {
     54 		case "darwin":
     55 			switch runtime.GOARCH {
     56 			case "arm", "arm64":
     57 				return false
     58 			}
     59 		case "android":
     60 			return false
     61 		}
     62 	}
     63 	return true
     64 }
     65 
     66 func runStreamSyslog(l net.Listener, done chan<- string, wg *sync.WaitGroup) {
     67 	for {
     68 		var c net.Conn
     69 		var err error
     70 		if c, err = l.Accept(); err != nil {
     71 			return
     72 		}
     73 		wg.Add(1)
     74 		go func(c net.Conn) {
     75 			defer wg.Done()
     76 			c.SetReadDeadline(time.Now().Add(5 * time.Second))
     77 			b := bufio.NewReader(c)
     78 			for ct := 1; !crashy || ct&7 != 0; ct++ {
     79 				s, err := b.ReadString('\n')
     80 				if err != nil {
     81 					break
     82 				}
     83 				done <- s
     84 			}
     85 			c.Close()
     86 		}(c)
     87 	}
     88 }
     89 
     90 func startServer(n, la string, done chan<- string) (addr string, sock io.Closer, wg *sync.WaitGroup) {
     91 	if n == "udp" || n == "tcp" {
     92 		la = "127.0.0.1:0"
     93 	} else {
     94 		// unix and unixgram: choose an address if none given
     95 		if la == "" {
     96 			// use ioutil.TempFile to get a name that is unique
     97 			f, err := ioutil.TempFile("", "syslogtest")
     98 			if err != nil {
     99 				log.Fatal("TempFile: ", err)
    100 			}
    101 			f.Close()
    102 			la = f.Name()
    103 		}
    104 		os.Remove(la)
    105 	}
    106 
    107 	wg = new(sync.WaitGroup)
    108 	if n == "udp" || n == "unixgram" {
    109 		l, e := net.ListenPacket(n, la)
    110 		if e != nil {
    111 			log.Fatalf("startServer failed: %v", e)
    112 		}
    113 		addr = l.LocalAddr().String()
    114 		sock = l
    115 		wg.Add(1)
    116 		go func() {
    117 			defer wg.Done()
    118 			runPktSyslog(l, done)
    119 		}()
    120 	} else {
    121 		l, e := net.Listen(n, la)
    122 		if e != nil {
    123 			log.Fatalf("startServer failed: %v", e)
    124 		}
    125 		addr = l.Addr().String()
    126 		sock = l
    127 		wg.Add(1)
    128 		go func() {
    129 			defer wg.Done()
    130 			runStreamSyslog(l, done, wg)
    131 		}()
    132 	}
    133 	return
    134 }
    135 
    136 func TestWithSimulated(t *testing.T) {
    137 	t.Parallel()
    138 	msg := "Test 123"
    139 	var transport []string
    140 	for _, n := range []string{"unix", "unixgram", "udp", "tcp"} {
    141 		if testableNetwork(n) {
    142 			transport = append(transport, n)
    143 		}
    144 	}
    145 
    146 	for _, tr := range transport {
    147 		done := make(chan string)
    148 		addr, sock, srvWG := startServer(tr, "", done)
    149 		defer srvWG.Wait()
    150 		defer sock.Close()
    151 		if tr == "unix" || tr == "unixgram" {
    152 			defer os.Remove(addr)
    153 		}
    154 		s, err := Dial(tr, addr, LOG_INFO|LOG_USER, "syslog_test")
    155 		if err != nil {
    156 			t.Fatalf("Dial() failed: %v", err)
    157 		}
    158 		err = s.Info(msg)
    159 		if err != nil {
    160 			t.Fatalf("log failed: %v", err)
    161 		}
    162 		check(t, msg, <-done)
    163 		s.Close()
    164 	}
    165 }
    166 
    167 func TestFlap(t *testing.T) {
    168 	net := "unix"
    169 	if !testableNetwork(net) {
    170 		t.Skipf("skipping on %s/%s; 'unix' is not supported", runtime.GOOS, runtime.GOARCH)
    171 	}
    172 
    173 	done := make(chan string)
    174 	addr, sock, srvWG := startServer(net, "", done)
    175 	defer srvWG.Wait()
    176 	defer os.Remove(addr)
    177 	defer sock.Close()
    178 
    179 	s, err := Dial(net, addr, LOG_INFO|LOG_USER, "syslog_test")
    180 	if err != nil {
    181 		t.Fatalf("Dial() failed: %v", err)
    182 	}
    183 	msg := "Moo 2"
    184 	err = s.Info(msg)
    185 	if err != nil {
    186 		t.Fatalf("log failed: %v", err)
    187 	}
    188 	check(t, msg, <-done)
    189 
    190 	// restart the server
    191 	_, sock2, srvWG2 := startServer(net, addr, done)
    192 	defer srvWG2.Wait()
    193 	defer sock2.Close()
    194 
    195 	// and try retransmitting
    196 	msg = "Moo 3"
    197 	err = s.Info(msg)
    198 	if err != nil {
    199 		t.Fatalf("log failed: %v", err)
    200 	}
    201 	check(t, msg, <-done)
    202 
    203 	s.Close()
    204 }
    205 
    206 func TestNew(t *testing.T) {
    207 	if LOG_LOCAL7 != 23<<3 {
    208 		t.Fatalf("LOG_LOCAL7 has wrong value")
    209 	}
    210 	if testing.Short() {
    211 		// Depends on syslog daemon running, and sometimes it's not.
    212 		t.Skip("skipping syslog test during -short")
    213 	}
    214 
    215 	s, err := New(LOG_INFO|LOG_USER, "the_tag")
    216 	if err != nil {
    217 		t.Fatalf("New() failed: %s", err)
    218 	}
    219 	// Don't send any messages.
    220 	s.Close()
    221 }
    222 
    223 func TestNewLogger(t *testing.T) {
    224 	if testing.Short() {
    225 		t.Skip("skipping syslog test during -short")
    226 	}
    227 	f, err := NewLogger(LOG_USER|LOG_INFO, 0)
    228 	if f == nil {
    229 		t.Error(err)
    230 	}
    231 }
    232 
    233 func TestDial(t *testing.T) {
    234 	if testing.Short() {
    235 		t.Skip("skipping syslog test during -short")
    236 	}
    237 	f, err := Dial("", "", (LOG_LOCAL7|LOG_DEBUG)+1, "syslog_test")
    238 	if f != nil {
    239 		t.Fatalf("Should have trapped bad priority")
    240 	}
    241 	f, err = Dial("", "", -1, "syslog_test")
    242 	if f != nil {
    243 		t.Fatalf("Should have trapped bad priority")
    244 	}
    245 	l, err := Dial("", "", LOG_USER|LOG_ERR, "syslog_test")
    246 	if err != nil {
    247 		t.Fatalf("Dial() failed: %s", err)
    248 	}
    249 	l.Close()
    250 }
    251 
    252 func check(t *testing.T, in, out string) {
    253 	tmpl := fmt.Sprintf("<%d>%%s %%s syslog_test[%%d]: %s\n", LOG_USER+LOG_INFO, in)
    254 	if hostname, err := os.Hostname(); err != nil {
    255 		t.Error("Error retrieving hostname")
    256 	} else {
    257 		var parsedHostname, timestamp string
    258 		var pid int
    259 		if n, err := fmt.Sscanf(out, tmpl, &timestamp, &parsedHostname, &pid); n != 3 || err != nil || hostname != parsedHostname {
    260 			t.Errorf("Got %q, does not match template %q (%d %s)", out, tmpl, n, err)
    261 		}
    262 	}
    263 }
    264 
    265 func TestWrite(t *testing.T) {
    266 	t.Parallel()
    267 	tests := []struct {
    268 		pri Priority
    269 		pre string
    270 		msg string
    271 		exp string
    272 	}{
    273 		{LOG_USER | LOG_ERR, "syslog_test", "", "%s %s syslog_test[%d]: \n"},
    274 		{LOG_USER | LOG_ERR, "syslog_test", "write test", "%s %s syslog_test[%d]: write test\n"},
    275 		// Write should not add \n if there already is one
    276 		{LOG_USER | LOG_ERR, "syslog_test", "write test 2\n", "%s %s syslog_test[%d]: write test 2\n"},
    277 	}
    278 
    279 	if hostname, err := os.Hostname(); err != nil {
    280 		t.Fatalf("Error retrieving hostname")
    281 	} else {
    282 		for _, test := range tests {
    283 			done := make(chan string)
    284 			addr, sock, srvWG := startServer("udp", "", done)
    285 			defer srvWG.Wait()
    286 			defer sock.Close()
    287 			l, err := Dial("udp", addr, test.pri, test.pre)
    288 			if err != nil {
    289 				t.Fatalf("syslog.Dial() failed: %v", err)
    290 			}
    291 			defer l.Close()
    292 			_, err = io.WriteString(l, test.msg)
    293 			if err != nil {
    294 				t.Fatalf("WriteString() failed: %v", err)
    295 			}
    296 			rcvd := <-done
    297 			test.exp = fmt.Sprintf("<%d>", test.pri) + test.exp
    298 			var parsedHostname, timestamp string
    299 			var pid int
    300 			if n, err := fmt.Sscanf(rcvd, test.exp, &timestamp, &parsedHostname, &pid); n != 3 || err != nil || hostname != parsedHostname {
    301 				t.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, test.exp, n, err)
    302 			}
    303 		}
    304 	}
    305 }
    306 
    307 func TestConcurrentWrite(t *testing.T) {
    308 	addr, sock, srvWG := startServer("udp", "", make(chan string, 1))
    309 	defer srvWG.Wait()
    310 	defer sock.Close()
    311 	w, err := Dial("udp", addr, LOG_USER|LOG_ERR, "how's it going?")
    312 	if err != nil {
    313 		t.Fatalf("syslog.Dial() failed: %v", err)
    314 	}
    315 	var wg sync.WaitGroup
    316 	for i := 0; i < 10; i++ {
    317 		wg.Add(1)
    318 		go func() {
    319 			defer wg.Done()
    320 			err := w.Info("test")
    321 			if err != nil {
    322 				t.Errorf("Info() failed: %v", err)
    323 				return
    324 			}
    325 		}()
    326 	}
    327 	wg.Wait()
    328 }
    329 
    330 func TestConcurrentReconnect(t *testing.T) {
    331 	crashy = true
    332 	defer func() { crashy = false }()
    333 
    334 	const N = 10
    335 	const M = 100
    336 	net := "unix"
    337 	if !testableNetwork(net) {
    338 		net = "tcp"
    339 		if !testableNetwork(net) {
    340 			t.Skipf("skipping on %s/%s; neither 'unix' or 'tcp' is supported", runtime.GOOS, runtime.GOARCH)
    341 		}
    342 	}
    343 	done := make(chan string, N*M)
    344 	addr, sock, srvWG := startServer(net, "", done)
    345 	if net == "unix" {
    346 		defer os.Remove(addr)
    347 	}
    348 
    349 	// count all the messages arriving
    350 	count := make(chan int)
    351 	go func() {
    352 		ct := 0
    353 		for range done {
    354 			ct++
    355 			// we are looking for 500 out of 1000 events
    356 			// here because lots of log messages are lost
    357 			// in buffers (kernel and/or bufio)
    358 			if ct > N*M/2 {
    359 				break
    360 			}
    361 		}
    362 		count <- ct
    363 	}()
    364 
    365 	var wg sync.WaitGroup
    366 	wg.Add(N)
    367 	for i := 0; i < N; i++ {
    368 		go func() {
    369 			defer wg.Done()
    370 			w, err := Dial(net, addr, LOG_USER|LOG_ERR, "tag")
    371 			if err != nil {
    372 				t.Errorf("syslog.Dial() failed: %v", err)
    373 				return
    374 			}
    375 			defer w.Close()
    376 			for i := 0; i < M; i++ {
    377 				err := w.Info("test")
    378 				if err != nil {
    379 					t.Errorf("Info() failed: %v", err)
    380 					return
    381 				}
    382 			}
    383 		}()
    384 	}
    385 	wg.Wait()
    386 	sock.Close()
    387 	srvWG.Wait()
    388 	close(done)
    389 
    390 	select {
    391 	case <-count:
    392 	case <-time.After(100 * time.Millisecond):
    393 		t.Error("timeout in concurrent reconnect")
    394 	}
    395 }
    396