// Unit tests for the package go-susi/message. func Message_test() { fmt.Printf("\n==== message ===\n\n") getFJU() // clean up remnants from DB_test() go func() { defer func() { recover() }() message.DistributeForeignJobUpdates() }() // This is a nasty trick. Send nil over the channel to cause a // a panic in the message.DistributeForeignJobUpdates() // goroutine so that it will terminate. // ATM it's not actually necessary to make it terminate but it's // a precaution against future test code that may not expect someone // to be reading from this channel. defer func() { db.ForeignJobUpdates <- nil }() nohost := error_string(<-message.Peer("Broken").Ask("foo", "")) if nohost == "lookup Broken: No address associated with hostname" { nohost = "lookup Broken: no such host" } check(hasWords(nohost, "lookup", "no such host"), "") check(error_string(<-message.Peer("192.168.250.128:55").Ask("foo", "")), "PeerConnection.Ask: No key known for peer 192.168.250.128:55") check(hasWords(error_string(<-message.Peer("127.0.0.1:55551").Ask("foo", "bar")), "connection refused"), "") listen() defer func() { listen_stop() }() listen_address = config.IP + ":" + listen_port init_keys() keys[0] = "MessageTestKey" db.ServerUpdate(hash("xml(key(%v)source(%v))", keys[0], listen_address)) message.Peer(listen_address).SetGoSusi(true) check(message.Peer(listen_address).IsGoSusi(), true) message.Peer(listen_address).SetGoSusi(false) check(message.Peer(listen_address).IsGoSusi(), false) message.Peer(listen_address).SetGoSusi(true) check(message.Peer(listen_address).IsGoSusi(), true) check(message.Peer(listen_address).Downtime(), 0) t0 := time.Now() message.Peer(listen_address).Tell("<xml><header>Hallo</header></xml>", keys[0]) check(wait(t0, "Hallo").XML.Text("header"), "Hallo") t0 = time.Now() message.Peer(listen_address).Tell("<xml><header>Aloha</header></xml>", "") check(wait(t0, "Aloha").XML.Text("header"), "Aloha") // suppress error logs oldlevel := util.LogLevel util.LogLevel = -1 check(error_string(<-message.Peer(listen_address).Ask("<xml><header>gosa_query_jobdb</header></xml>", config.ModuleKey["[GOsaPackages]"])), "") check(error_string(<-message.Peer(listen_address).Ask("<xml><header>whatever</header></xml>", config.ModuleKey["[GOsaPackages]"])), "Communication error in Ask()") for repcount := 0; repcount < 2; repcount++ { // shut down listener listen_stop() // wait 3 seconds and check downtime time.Sleep(time.Duration(3+repcount) * time.Second) downtime := int64((message.Peer(listen_address).Downtime() + 500*time.Millisecond) / time.Second) check(downtime, 3+repcount) // verify that connection is really down t0 = time.Now() message.Peer(listen_address).Tell("<xml><header>down</header></xml>", keys[0]) check(wait(t0, "down").XML.Text("header"), "") // restart listener listen() // send message and check if the listener receives it t0 = time.Now() message.Peer(listen_address).Tell("<xml><header>Wuseldusel</header></xml>", keys[0]) check(wait(t0, "Wuseldusel").XML.Text("header"), "Wuseldusel") // check that downtime has stopped check(message.Peer(listen_address).Downtime(), 0) } // set maximum downtime before removal defer func(mpd time.Duration) { config.MaxPeerDowntime = mpd }(config.MaxPeerDowntime) config.MaxPeerDowntime = 4 * time.Second time.Sleep(6 * time.Second) // we need to wait for the old 10s pinger to expire // stop listener listen_stop() // ping to make handleConnection() aware that we're down message.Peer(listen_address).Tell("<xml><header>Ping</header></xml>", keys[0]) time.Sleep(1 * time.Second) message.Peer(listen_address).Tell("<xml><header>Ping</header></xml>", keys[0]) time.Sleep(1 * time.Second) // check we haven't been removed from serverdb, yet check(db.ServerKeys(listen_address), []string{keys[0]}) time.Sleep(3 * time.Second) // ping again. This time handleConnection() should notice we're down to long message.Peer(listen_address).Tell("<xml><header>Ping</header></xml>", keys[0]) time.Sleep(1 * time.Second) // check that we've been removed from serverdb check(db.ServerKeys(listen_address), []string{}) // restore old log level time.Sleep(1 * time.Second) util.LogLevel = oldlevel nohost = error_string(<-message.Peer("broken").Ask("", "")) if nohost == "lookup broken: No address associated with hostname" { nohost = "lookup broken: no such host" } check(hasWords(nohost, "lookup", "no such host"), "") check(error_string(<-message.Peer("doesnotexist.domain:10").Ask("", "")), "lookup doesnotexist.domain: no such host") util.LoggersSuspend() oldloglevel := util.LogLevel defer func() { util.LoggersRestore(); util.LogLevel = oldloglevel }() var buffy bytes.Buffer util.LoggerAdd(&buffy) util.LogLevel = 2 // we want all messages, including INFO! and DEBUG! client := xml.NewHash("xml", "header", "new_foreign_client") client.Add("source", config.ServerSourceAddress) client.Add("target", "127.0.0.1:12345") client.Add("client", listen_address) client.Add("macaddress", "11:22:33:44:55:66") keys[len(keys)-1] = "weissnich" client.Add("key", keys[len(keys)-1]) db.ClientUpdate(client) t0 = time.Now() message.Client(listen_address).Tell("<xml><header>Alle meine Entchen</header></xml>", 0) time.Sleep(reply_timeout) check(get(t0), []*queueElement{}) check(hasWords(buffy.String(), "ERROR", "Cannot send message"), "") buffy.Reset() t0 = time.Now() message.Client(listen_address).Tell("<xml><header>Alle meine Hündchen</header></xml>", -1200*time.Millisecond) time.Sleep(reply_timeout) check(get(t0), []*queueElement{}) check(hasWords(buffy.String(), "ERROR", "Cannot send message"), "") buffy.Reset() t0 = time.Now() message.Client(listen_address).Tell("<xml><header>Alle meine Kätzchen</header></xml>", 2*time.Second) time.Sleep(3 * time.Second) check(get(t0), []*queueElement{}) check(hasWords(buffy.String(), "ERROR", "Cannot send message", "Attempt #1", "Attempt #2"), "") buffy.Reset() t0 = time.Now() message.Client(listen_address).Tell("<xml><header>Alle meine Häschen</header></xml>", 3*time.Second) time.Sleep(1500 * time.Millisecond) listen() time.Sleep(1 * time.Second) x := get(t0) if check(len(x), 1) { check(x[0].XML.Text("header"), "Alle meine Häschen") check(x[0].Key, keys[len(keys)-1]) } check(hasWords(buffy.String(), "Attempt #1", "Attempt #2", "Successfully sent message"), "") buffy.Reset() t0 = time.Now() message.Client(listen_address).Tell("<xml><header>Alle meine Vöglein</header></xml>", -1200*time.Millisecond) time.Sleep(reply_timeout) x = get(t0) if check(len(x), 1) { check(x[0].XML.Text("header"), "Alle meine Vöglein") check(x[0].Key, keys[len(keys)-1]) } check(hasWords(buffy.String(), "Successfully sent message"), "") listen_stop() }
func testLogging() { // Check that os.Stderr is the (only) default logger check(util.LoggersCount(), 1) util.LoggerRemove(os.Stderr) check(util.LoggersCount(), 0) // Check that default loglevel is 0 check(util.LogLevel, 0) flushy := new(FlushableBuffer) synchy := new(SyncableBuffer) util.LoggerAdd(flushy) check(util.LoggersCount(), 1) util.LoggerAdd(synchy) check(util.LoggersCount(), 2) util.LogLevel = 4 defer func() { util.LogLevel = 0 }() oldfac := util.BacklogFactor defer func() { util.BacklogFactor = oldfac }() util.BacklogFactor = 4 util.Log(0, "a0") // 0 time.Sleep(200 * time.Millisecond) for i := 1; i <= 4; i++ { util.Log(i, "a%d", i) } // 1,2,3,4 for i := 0; i <= 4; i++ { util.Log(i, "b%d", i) } // 0,1,2,3 for i := 0; i <= 4; i++ { util.Log(i, "c%d", i) } // 0,1,2 for i := 0; i <= 4; i++ { util.Log(i, "d%d", i) } // 0,1 for i := 0; i <= 4; i++ { util.Log(i, "e%d", i) } // 0,1 util.Log(1, "x") // should be logged because when this Log() is executed, the backlog is only 15 long util.Log(1, "y") // should NOT be logged after the previous "x" the backlog is 16=4*BacklogFactor long check(flushy.Flushes, 0) check(synchy.Flushes, 0) time.Sleep(2 * time.Second) check(flushy.Flushes, 1) check(synchy.Flushes, 1) util.Log(5, "Shouldnotbelogged!") util.Log(4, "Shouldbelogged!") time.Sleep(200 * time.Millisecond) check(flushy.Flushes, 2) check(synchy.Flushes, 2) util.LoggersSuspend() check(util.LoggersCount(), 0) util.LoggerAdd(os.Stderr) check(util.LoggersCount(), 1) util.LoggersSuspend() check(util.LoggersCount(), 0) util.Log(0, "This should disappear in the void") buffy := new(bytes.Buffer) util.LoggerAdd(buffy) joke := "Sagt die Katze zum Verkäufer: Ich hab nicht genug Mäuse. Kann ich auch in Ratten zahlen?" util.Log(0, joke) time.Sleep(200 * time.Millisecond) check(strings.Index(buffy.String(), joke) >= 0, true) check(util.LoggersCount(), 1) util.LoggersRestore() check(util.LoggersCount(), 1) util.LoggersRestore() check(util.LoggersCount(), 2) util.Log(0, "foo") time.Sleep(200 * time.Millisecond) lines := flushy.Buf.Split("\n") for i := range lines { if strings.Index(lines[i], "missing") < 0 { idx := strings.LastIndex(lines[i], " ") lines[i] = lines[i][idx+1:] } else { idx := strings.Index(lines[i], " missing") lines[i] = lines[i][idx-2 : idx] } } check(lines, []string{"a0", "a1", "a2", "a3", "a4", "b0", "b1", "b2", "b3", "c0", "c1", "c2", "d0", "d1", "e0", "e1", "x", "10", "Shouldbelogged!", "foo", ""}) check(flushy.Buf.String(), synchy.Buf.String()) // Reset loggers so that only os.Stderr is a logger for util.LoggersCount() > 0 { util.LoggersRestore() } util.LoggerAdd(os.Stderr) }
// Unit tests for the package security. func Security_test() { fmt.Printf("\n==== security ===\n\n") config.CACertPath = []string{"testdata/certs/ca.cert"} // do not spam console with expected errors but do // store them in the log file (if any is configured) util.LoggerRemove(os.Stderr) defer util.LoggerAdd(os.Stderr) cli, srv := tlsTest("1", "1") check(cli != nil, true) check(srv != nil, true) cli, srv = tlsTest("1", "2") check(cli != nil, true) check(srv != nil, true) cli, srv = tlsTest("nocert", "1") check(cli, nil) check(srv, nil) cli, srv = tlsTest("signedbywrongca", "1") check(cli != nil, true) check(srv, nil) cli, srv = tlsTest("1", "signedbywrongca") check(cli, nil) check(srv != nil, true) cli, srv = tlsTest("local", "2") check(cli != nil, true) check(srv != nil, true) cli, srv = tlsTest("badip", "2") check(cli != nil, true) check(srv, nil) cli, srv = tlsTest("badname", "2") check(cli != nil, true) check(srv, nil) cli, srv = tlsTest("localname", "2") check(cli != nil, true) check(srv, nil) // because *ocalhost does not match the actual resolved name security.SetMyServer("8.8.8.8") cli, srv = tlsTest("myserver", "2") check(cli != nil, true) check(srv, nil) security.SetMyServer("127.0.0.1") cli, srv = tlsTest("myserver", "2") check(cli != nil, true) check(srv != nil, true) cli, srv = tlsTest("limits", "2") check(cli != nil, true) if check(srv != nil, true) { check(srv.Limits.TotalTime, time.Duration(98765)*time.Millisecond) check(srv.Limits.TotalBytes, 123456789012345) check(srv.Limits.MessageBytes, 76767542) check(srv.Limits.ConnPerHour, 3289) check(srv.Limits.ConnParallel, 348201284) check(srv.Limits.MaxLogFiles, 700499) check(srv.Limits.MaxAnswers, 4) check(srv.Limits.CommunicateWith, []string{"foo.tvc.muenchen.de:8089", "nobody", "1.2.3.4", "*"}) } }
// Unit tests for the package github.com/mbenkmann/golib/util. func Util_test() { fmt.Printf("\n==== util ===\n\n") addr, err := util.Resolve("1.2.3.4", "") check(err, nil) check(addr, "1.2.3.4") addr, err = util.Resolve("1.2.3.4:5", "") check(err, nil) check(addr, "1.2.3.4:5") addr, err = util.Resolve("::1:5", "") check(err, nil) check(addr, "[::1:5]") addr, err = util.Resolve("localhost:65535", "") check(err, nil) check(addr, "127.0.0.1:65535") addr, err = util.Resolve("localhost", "") check(err, nil) check(addr, "127.0.0.1") addr, err = util.Resolve("::1", "") check(err, nil) check(addr, "127.0.0.1") addr, err = util.Resolve("[::1]", "") check(err, nil) check(addr, "127.0.0.1") addr, err = util.Resolve("[::1]:12345", "") check(err, nil) check(addr, "127.0.0.1:12345") addr, err = util.Resolve("localhost:65535", "foo") check(err, nil) check(addr, "foo:65535") addr, err = util.Resolve("localhost", "foo") check(err, nil) check(addr, "foo") addr, err = util.Resolve("::1", "foo") check(err, nil) check(addr, "foo") addr, err = util.Resolve("[::1]", "foo") check(err, nil) check(addr, "foo") addr, err = util.Resolve("[::1]:12345", "foo") check(err, nil) check(addr, "foo:12345") addr, err = util.Resolve("", "") check(hasWords(err, "no", "such", "host"), "") check(addr, "") addr, err = util.Resolve(":10", "") check(hasWords(err, "no", "such", "host"), "") check(addr, ":10") check(util.WaitForDNS(3*time.Second), true) h, _ := exec.Command("hostname").CombinedOutput() hostname := strings.TrimSpace(string(h)) ipp, _ := exec.Command("hostname", "-I").CombinedOutput() ips := strings.Fields(strings.TrimSpace(string(ipp))) addr, err = util.Resolve(hostname+":234", config.IP) check(err, nil) ip := "" for _, ip2 := range ips { if addr == ip2+":234" { ip = ip2 } } check(addr, ip+":234") testLogging() buf := make([]byte, 80) for i := range buf { buf[i] = byte(util_test_rng.Intn(26) + 'a') } crap1 := &crappyConnection1{} n, err := util.WriteAll(crap1, buf) check(string(*crap1), string(buf)) check(n, len(buf)) check(err, nil) crap2 := &crappyConnection2{} n, err = util.WriteAll(crap2, buf) check(string(*crap2), string(buf)) check(n, len(buf)) check(err, nil) stalled1 := &stalledConnection1{} n, err = util.WriteAll(stalled1, buf) check(string(*stalled1), string(buf[0:16])) check(n, 16) check(err, io.ErrShortWrite) stalled2 := &stalledConnection2{} n, err = util.WriteAll(stalled2, buf) check(string(*stalled2), string(buf[0:16])) check(n, 16) check(err, io.ErrShortWrite) broken := &brokenConnection{} n, err = util.WriteAll(broken, buf) check(string(*broken), string(buf[0:16])) check(n, 16) check(err, io.ErrClosedPipe) panicker := func() { foobar = "bar" panic("foo") } var buffy bytes.Buffer util.LoggersSuspend() util.LoggerAdd(&buffy) defer util.LoggersRestore() util.WithPanicHandler(panicker) time.Sleep(200 * time.Millisecond) // make sure log message is written out check(foobar, "bar") check(len(buffy.String()) > 10, true) listener, err := net.Listen("tcp", "127.0.0.1:39390") if err != nil { panic(err) } go func() { r, err := listener.Accept() if err != nil { panic(err) } buf := make([]byte, 1) r.Read(buf) time.Sleep(10 * time.Second) r.Read(buf) }() long := make([]byte, 10000000) longstr := string(long) buffy.Reset() t0 := time.Now() util.SendLnTo("127.0.0.1:39390", longstr, 5*time.Second) duration := time.Since(t0) check(duration > 4*time.Second && duration < 6*time.Second, true) time.Sleep(200 * time.Millisecond) // make sure log message is written out check(strings.Contains(buffy.String(), "ERROR"), true) go func() { conn, err := listener.Accept() if err != nil { panic(err) } ioutil.ReadAll(conn) }() long = make([]byte, 10000000) longstr = string(long) buffy.Reset() t0 = time.Now() util.SendLnTo("127.0.0.1:39390", longstr, 5*time.Second) duration = time.Since(t0) check(duration < 2*time.Second, true) time.Sleep(200 * time.Millisecond) // make sure log message is written out check(buffy.String(), "") // Test that ReadLn() times out properly go func() { _, err := net.Dial("tcp", "127.0.0.1:39390") if err != nil { panic(err) } }() conn, err := listener.Accept() if err != nil { panic(err) } t0 = time.Now() st, err := util.ReadLn(conn, 5*time.Second) duration = time.Since(t0) check(duration > 4*time.Second && duration < 6*time.Second, true) check(st, "") check(hasWords(err, "timeout"), "") // Test that ReadLn() returns io.EOF if last line not terminated by \n go func() { conn, err := net.Dial("tcp", "127.0.0.1:39390") if err != nil { panic(err) } conn.Write([]byte("foo\r")) conn.Close() }() conn, err = listener.Accept() if err != nil { panic(err) } st, err = util.ReadLn(conn, 5*time.Second) check(err, io.EOF) check(st, "foo") go func() { conn, err := net.Dial("tcp", "127.0.0.1:39390") if err != nil { panic(err) } conn.Write([]byte("\r\r\n\rfo\ro\nbar\r\nfoxtrott")) conn.Close() }() conn, err = listener.Accept() if err != nil { panic(err) } // Test proper trimming of multiple \r st, err = util.ReadLn(conn, 0) check(err, nil) check(st, "") // Test that the empty first line has actually been read // and that the next ReadLn() reads the 2nd line // Also test that negative timeouts work the same as timeout==0 // Also test that \r is not trimmed at start and within line. st, err = util.ReadLn(conn, -1*time.Second) check(err, nil) check(st, "\rfo\ro") // Check 3rd line st, err = util.ReadLn(conn, 0) check(err, nil) check(st, "bar") // Check 4th line and io.EOF error st, err = util.ReadLn(conn, 0) check(err, io.EOF) check(st, "foxtrott") // Test that delayed reads work with timeout==0 go func() { conn, err := net.Dial("tcp", "127.0.0.1:39390") if err != nil { panic(err) } time.Sleep(1 * time.Second) _, err = conn.Write([]byte("foo\r\n")) if err != nil { panic(err) } time.Sleep(2 * time.Second) }() conn, err = listener.Accept() if err != nil { panic(err) } t0 = time.Now() st, err = util.ReadLn(conn, time.Duration(0)) duration = time.Since(t0) check(duration < 2*time.Second, true) check(duration > 800*time.Millisecond, true) check(err, nil) check(st, "foo") counter := util.Counter(13) var b1 UintArray = make([]uint64, 100) var b2 UintArray = make([]uint64, 100) done := make(chan bool) fill := func(b UintArray) { for i := 0; i < 100; i++ { b[i] = <-counter time.Sleep(1 * time.Millisecond) } done <- true } go fill(b1) go fill(b2) <-done <-done check(sort.IsSorted(&b1), true) check(sort.IsSorted(&b2), true) var b3 UintArray = make([]uint64, 200) i := 0 j := 0 k := 0 for i < 100 || j < 100 { if i == 100 { b3[k] = b2[j] j++ k++ continue } if j == 100 { b3[k] = b1[i] i++ k++ continue } if b1[i] == b2[j] { check(b1[i] != b2[j], true) break } if b1[i] < b2[j] { b3[k] = b1[i] i++ } else { b3[k] = b2[j] j++ } k++ } one_streak := true b5 := make([]uint64, 200) for i := 0; i < 200; i++ { if i < 100 && b1[i] != uint64(13+i) && b2[i] != uint64(13+i) { one_streak = false } b5[i] = uint64(13 + i) } check(b3, b5) check(one_streak, false) // Check whether goroutines were actually executed concurrently rather than in sequence tempdir, err := ioutil.TempDir("", "util-test-") if err != nil { panic(err) } defer os.RemoveAll(tempdir) fpath := tempdir + "/foo.log" logfile := util.LogFile(fpath) check(logfile.Close(), nil) n, err = util.WriteAll(logfile, []byte("Test")) check(err, nil) check(n, 4) check(logfile.Close(), nil) n, err = util.WriteAll(logfile, []byte("12")) check(err, nil) check(n, 2) n, err = util.WriteAll(logfile, []byte("3")) check(err, nil) check(n, 1) check(os.Rename(fpath, fpath+".old"), nil) n, err = util.WriteAll(logfile, []byte("Fo")) check(err, nil) check(n, 2) f2, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) f2.Write([]byte("o")) f2.Close() n, err = util.WriteAll(logfile, []byte("bar")) check(err, nil) check(n, 3) check(logfile.Close(), nil) data, err := ioutil.ReadFile(fpath) check(err, nil) if err == nil { check(string(data), "Foobar") } data, err = ioutil.ReadFile(fpath + ".old") check(err, nil) if err == nil { check(string(data), "Test123") } test_time := time.Date(2013, time.January, 20, 14, 7, 21, 0, time.Local) check(util.MakeTimestamp(test_time), "20130120140721") test_time = time.Date(2013, time.January, 20, 14, 7, 21, 0, time.UTC) check(util.MakeTimestamp(test_time), "20130120140721") test_time = time.Date(2013, time.January, 20, 14, 7, 21, 0, time.FixedZone("Fooistan", 45678)) check(util.MakeTimestamp(test_time), "20130120140721") illegal := time.Unix(0, 0) buffy.Reset() check(util.ParseTimestamp(""), illegal) time.Sleep(200 * time.Millisecond) // make sure log message is written out check(strings.Contains(buffy.String(), "ERROR"), true) buffy.Reset() check(util.ParseTimestamp("20139910101010"), illegal) time.Sleep(200 * time.Millisecond) // make sure log message is written out check(strings.Contains(buffy.String(), "ERROR"), true) check(util.ParseTimestamp("20131110121314"), time.Date(2013, time.November, 10, 12, 13, 14, 0, time.Local)) check(util.MakeTimestamp(util.ParseTimestamp(util.MakeTimestamp(test_time))), util.MakeTimestamp(test_time)) test_time = test_time.Add(2400 * time.Hour) check(util.MakeTimestamp(util.ParseTimestamp(util.MakeTimestamp(test_time))), util.MakeTimestamp(test_time)) test_time = test_time.Add(2400 * time.Hour) check(util.MakeTimestamp(util.ParseTimestamp(util.MakeTimestamp(test_time))), util.MakeTimestamp(test_time)) test_time = test_time.Add(2400 * time.Hour) check(util.MakeTimestamp(util.ParseTimestamp(util.MakeTimestamp(test_time))), util.MakeTimestamp(test_time)) test_time = test_time.Add(2400 * time.Hour) check(util.MakeTimestamp(util.ParseTimestamp(util.MakeTimestamp(test_time))), util.MakeTimestamp(test_time)) diff := time.Since(util.ParseTimestamp(util.MakeTimestamp(time.Now()))) if diff < time.Second { diff = 0 } check(diff, time.Duration(0)) t0 = time.Now() util.WaitUntil(t0.Add(-10 * time.Second)) util.WaitUntil(t0.Add(-100 * time.Minute)) dur := time.Now().Sub(t0) if dur < 1*time.Second { dur = 0 } check(dur, 0) t0 = time.Now() util.WaitUntil(t0.Add(1200 * time.Millisecond)) dur = time.Now().Sub(t0) if dur >= 1200*time.Millisecond && dur <= 1300*time.Millisecond { dur = 1200 * time.Millisecond } check(dur, 1200*time.Millisecond) mess := "WaitUntil(Jesus first birthday) takes forever" go func() { util.WaitUntil(time.Date(1, time.December, 25, 0, 0, 0, 0, time.UTC)) mess = "" }() time.Sleep(100 * time.Millisecond) check(mess, "") mess = "WaitUntil(1000-11-10 00:00:00) takes forever" go func() { util.WaitUntil(time.Date(1000, time.October, 11, 0, 0, 0, 0, time.UTC)) mess = "" }() time.Sleep(100 * time.Millisecond) check(mess, "") testBase64() }
func main() { // Intercept signals asap (in particular intercept SIGTTOU before the first output) signals := make(chan os.Signal, 32) signals_to_watch := []os.Signal{syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGTTOU, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT} signal.Notify(signals, signals_to_watch...) config.Init() config.ReadArgs(os.Args[1:]) if config.PrintVersion { fmt.Printf(`go-susi %v (revision %v) Copyright (c) 2013 Matthias S. Benkmann This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. `, config.Version, config.Revision) } if config.PrintHelp { fmt.Println(`USAGE: go-susi [args] --help print this text and exit --version print version and exit --stats print sistats info from running go-susi process -v print operator debug messages (INFO) -vv print developer debug messages (DEBUG) ATTENTION! developer messages include keys! -f start with a fresh database; discard old /var/lib/go-susi --test=<dir> test mode: * read config files from <dir> instead of /etc/gosa-si * use <dir>/go-susi.log as log file * use <dir> as database directory instead /var/lib/go-susi -c <file> read config from <file> instead of default location `) } if config.PrintVersion || config.PrintHelp { os.Exit(0) } config.ReadConfig() config.ReadCertificates() // after config.ReadConfig() if config.TLSRequired && config.TLSServerConfig == nil { util.Log(0, "ERROR! No cert, no keys => no service") util.LoggersFlush(5 * time.Second) os.Exit(1) } logfile, err := os.OpenFile(config.LogFilePath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) if err != nil { util.Log(0, "ERROR! %v", err) // Do not exit. We can go on without logging to a file. } else { logfile.Close() // will be re-opened on the first write // Add file to loggers list. os.Stderr is on it by default. util.LoggerAdd(util.LogFile(logfile.Name())) } util.LogLevel = config.LogLevel if config.PrintStats { // We nead ReadNetwork() to determine config.IP which is necessary // for TLS certificate validation. We call this inside the if config.Printstats // block instead of outside because for a go-susi daemon it is important // to wait for DNS before calling ReadNetwork() which is something // we don't want to do for the --stats call. config.ReadNetwork() code := printStats() util.LoggersFlush(5 * time.Second) os.Exit(code) } util.Log(0, "=============================================================================") util.Log(0, "INFO! go-susi %v started", config.Version) if !config.RunServer { util.Log(1, "INFO! No ldap-admin-dn configured => Will run in client-only mode") } util.Log(1, "INFO! Expecting standard clients to communicate on these ports: %v", config.ClientPorts) util.Log(1, "INFO! Waiting up to 5 minutes for DNS to be available") if !util.WaitForDNS(5 * time.Minute) { util.Log(0, "ERROR! DNS not available") util.LoggersFlush(5 * time.Second) os.Exit(1) } util.Log(1, "INFO! DNS available") config.ReadNetwork() // after config.ReadConfig() if config.TLSServerConfig != nil { util.Log(1, "INFO! [SECURITY] CA certificate:\n%v", security.CertificateInfo(config.CACert[0])) util.Log(1, "INFO! [SECURITY] My certificate:\n%v", security.CertificateInfo(config.TLSServerConfig.Certificates[0].Leaf)) } // ATTENTION! DO NOT MOVE THE FOLLOWING CODE FURTHER DOWN! // We want to try listening on our socket as early in the program as possible, // so that we can bail out if another go-susi instance is already running // before potentially damaging the databases. tcp_addr, err := net.ResolveTCPAddr("tcp4", config.ServerListenAddress) if err != nil { util.Log(0, "ERROR! ResolveTCPAddr: %v", err) util.LoggersFlush(5 * time.Second) os.Exit(1) } listener, err := net.ListenTCP("tcp4", tcp_addr) if err != nil { util.Log(0, "ERROR! ListenTCP: %v", err) util.LoggersFlush(5 * time.Second) os.Exit(1) } if config.RunServer { util.Log(1, "INFO! Waiting up to 5 minutes for %v to be available", config.LDAPURI) if !db.LDAPAvailable(5 * time.Minute) { util.Log(0, "ERROR! LDAP not available") util.LoggersFlush(5 * time.Second) os.Exit(1) } util.Log(1, "INFO! LDAP available") setConfigUnitTag() // after config.ReadNetwork() config.FAIBase = db.LDAPFAIBase() util.Log(1, "INFO! FAI base: %v", config.FAIBase) util.Log(1, "INFO! ou=servers.conf: %v", config.LDAPServerOUs) os.MkdirAll(path.Dir(config.JobDBPath), 0750) db.ServersInit() // after config.ReadNetwork() db.JobsInit() // after config.ReadConfig() db.ClientsInit() // after config.ReadConfig() db.HooksExecute(true) // after config.ReadConfig() action.Init() } // Create channels for receiving events. // The main() goroutine receives on all these channels // and spawns new goroutines to handle the incoming events. tcp_connections := make(chan *net.TCPConn, 32) // NOTE: signals channel is created at the beginning of main() util.Log(1, "INFO! Intercepting these signals: %v", signals_to_watch) util.Log(1, "INFO! Accepting gosa-si protocol connections on TCP port %v", strings.SplitN(config.ServerSourceAddress, ":", 2)[1]) go acceptConnections(listener, tcp_connections) go util.WithPanicHandler(faiProgressWatch) if config.RunServer { if config.FAIMonPort != "disabled" { util.Log(1, "INFO! Accepting FAI monitoring messages on TCP port %v", config.FAIMonPort) go faimon(":" + config.FAIMonPort) } util.Log(1, "INFO! Accepting TFTP requests on UDP port %v", config.TFTPPort) go tftp.ListenAndServe(":"+config.TFTPPort, config.TFTPRegexes, config.TFTPReplies) go message.CheckPossibleClients() go message.Broadcast_new_server() go message.DistributeForeignJobUpdates() } // http server for profiling //go func(){http.ListenAndServe("localhost:6060", nil)}() go message.RegistrationHandler() /******************** main event loop ***********************/ for { select { case sig := <-signals: //os.Signal if sig != syscall.SIGTTOU { // don't log SIGTTOU as that may cause another util.Log(1, "INFO! Received signal \"%v\"", sig) } if sig == syscall.SIGUSR2 && config.RunServer { db.HooksExecute(false) } if sig == syscall.SIGHUP || sig == syscall.SIGTERM || sig == syscall.SIGQUIT || sig == syscall.SIGINT { Shutdown = true util.Log(0, "WARNING! Shutting down!") util.Log(1, "INFO! Shutting down listener") listener.Close() if config.RunServer { wait := make(chan bool, 16) go func() { db.JobsShutdown(); wait <- true }() go func() { db.ServersShutdown(); wait <- true }() go func() { db.ClientsShutdown(); wait <- true }() <-wait // for jobdb <-wait // for serverdb <-wait // for clientdb } config.Shutdown() util.Log(1, "INFO! Average request processing time: %v", time.Duration((atomic.LoadInt64(&message.RequestProcessingTime)+50)/100)) util.Log(1, "INFO! Databases have been saved => Exit program") util.LoggersFlush(5 * time.Second) os.Exit(0) } case conn := <-tcp_connections: // *net.TCPConn if Shutdown { util.Log(1, "INFO! Rejecting TCP request from %v because of go-susi shutdown", conn.RemoteAddr()) conn.Close() } else { //util.Log(2, "DEBUG! Incoming TCP request from %v", conn.RemoteAddr()) go util.WithPanicHandler(func() { handle_request(conn) }) } } } }