func (m *Monitor) Start() error { if m.Name == "" { return e.New("empty name") } if m.Url == "" { return e.New("empty url") } if m.Periode == 0 { return e.New("periode must be greater than zero") } m.chclose = make(chan chan struct{}) go func() { for { select { case ch := <-m.chclose: ch <- struct{}{} return case <-time.After(m.Periode): resp := m.ping() select { case err := <-resp: if err == nil { if m.status != statusOk && m.OnUnFail != nil { err := m.OnUnFail(m) if err != nil { log.Errorf("OnUnFail for %v returned an error: %v", m.Name, err) } } m.status = statusOk continue } m.fail() continue case <-time.After(m.Timeout): log.Errorf("Ping timeout for %v", m.Name) m.fail() } } } }() return nil }
func (m *Monitor) ping() (resp chan error) { resp = make(chan error) go func() { log.DebugLevel().Printf("Pinging %v", m.Name) start := time.Now() err := ping.PingRawUrl(m.Url) if err != nil { log.Errorf("Ping failed for %v with error: %v", m.Name, e.Trace(e.Forward(err))) resp <- e.Forward(err) return } log.DebugLevel().Printf("Ping ok for %v (%v)", m.Name, time.Since(start)) resp <- nil }() return }
func (m *Monitor) fail() { m.count++ if m.Fails != 0 || m.Fails <= m.count { m.count = 0 m.status = statusFail if m.OnFail == nil { return } err := m.OnFail(m) if err != nil { log.Errorf("Onfail function on %v returned an error: %v", m.Name, err) } log.Printf("%v going to sleep for %v", m.Name, m.Sleep) select { case <-time.After(m.Sleep): case ch := <-m.chclose: ch <- struct{}{} return } } }
func (c *Client) client(addr string) (*Response, error) { ip, err := ipport(c.Interface, addr, "0") if err != nil { return nil, e.Push(err, ErrCantFindInt) } client, err := net.ResolveUDPAddr("udp", ip) if err != nil { return nil, e.Push(err, ErrCantFindInt) } c.conn, err = net.ListenUDP("udp", client) if err != nil { return nil, e.Push(err, ErrCantFindInt) } var dst *net.UDPAddr if c.iface.Flags&net.FlagLoopback == net.FlagLoopback { ip, err := ipport(c.Interface, addr, c.Port) if err != nil { return nil, e.Push(err, ErrCantFindInt) } dst, err = net.ResolveUDPAddr("udp", ip) if err != nil { return nil, e.Push(err, ErrCantFindInt) } } else if !c.NotMulticast && c.iface.Flags&net.FlagMulticast == net.FlagMulticast { dst, err = c.multicast(c.conn.LocalAddr()) if err != nil { return nil, e.Push(err, ErrCantFindInt) } } else if c.iface.Flags&net.FlagBroadcast == net.FlagBroadcast { dst, err = broadcast(c.conn.LocalAddr(), c.Port) if err != nil { return nil, e.Push(err, ErrCantFindInt) } } else { return nil, e.Push(e.New("interface isn't suported: %v", c.iface.Flags), ErrCantFindInt) } log.ProtoLevel().Tag("discover", "client").Printf("Local ip %v.", c.conn.LocalAddr()) log.ProtoLevel().Tag("discover", "client").Printf("Try to contact server in %v.", dst) now := time.Now() end := now.Add(c.Timeout) for d := now; d.Before(end) || d.Equal(end); d = time.Now() { req, err := c.Request(dst) if err != nil { return nil, e.Forward(err) } req.Id = c.Id req.Ip = c.conn.LocalAddr().String() err = c.encode(protoReq, req, dst) if e.Contains(err, "i/o timeout") { log.Errorf("Error %v -> %v: %v", c.conn.LocalAddr(), dst, err) continue } else if err != nil { return nil, e.Forward(err) } resp, err := c.response() if e.Contains(err, "i/o timeout") { log.Errorf("Error %v -> %v: %v", c.conn.LocalAddr(), dst, err) continue } else if err != nil { return nil, e.Forward(err) } c.Id = resp.Id err = c.encode(protoConfirm, resp.Id, dst) if e.Contains(err, "i/o timeout") { log.Errorf("Error %v -> %v: %v", c.conn.LocalAddr(), dst, err) continue } else if err != nil { return nil, e.Forward(err) } rp, err := c.response() if e.Contains(err, "i/o timeout") { log.Errorf("Error %v -> %v: %v", c.conn.LocalAddr(), dst, err) continue } else if err != nil { return nil, e.Forward(err) } if rp.Id != resp.Id { return nil, e.New("protocol fail wrong response") } go func(dst *net.UDPAddr) { for { select { case <-time.After(c.Keepalive): log.ProtoLevel().Tag("client", "discover").Printf("Send keep alive to %v", dst) err := c.keepalive(dst) if err != nil { log.Tag("client", "discover").Errorf("Keep alive to %v failed: %v", dst, err) return } case ch := <-c.stopKa: ch <- struct{}{} return } } }(dst) return resp, nil } return nil, e.New("can't find the server") }
func main() { println("Starting monlite...") // Configuration var opts options _, err := flags.Parse(&opts) if err != nil { log.Fatal("can't parse the command line options:", err) } cfg, err := ini.Load(opts.Conf) if err != nil { log.Fatal("Error reading configuratio file:", opts.Conf) } // Log stuff println("Log...") name := appname pid := os.Getpid() pidstr := strconv.FormatInt(int64(pid), 10) name = name + " (" + pidstr + ")" fnull, err := os.OpenFile("/dev/null", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) if err != nil { log.Fatal("open log file failed:", err) } defer fnull.Close() stderrBack := log.NewWriter(fnull).F(log.DefFormatter) if opts.Level != "" && opts.Level != "nolog" { level, err := log.ParseLevel(opts.Level) if err != nil { log.Fatal("Invalid log level.") } stderrBack = log.Filter( log.NewWriter(os.Stderr).F(log.DefFormatter), log.Op(log.Ge, "level", level), ) } logfile := cfg.Section("log").Key("file").MustString("/var/log/monlite.log") if opts.Log != "" { logfile = opts.Log } loglevel := cfg.Section("log").Key("level").MustString("debug") level, err := log.ParseLevel(loglevel) if err != nil { log.Fatal("Invalid log level.") } var fileBack log.LogBackend if logfile != "" { f, err := os.OpenFile(logfile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) if err != nil { log.Fatalf("open log file %v failed: %v", logfile, err) } defer f.Close() fileBack = log.Filter( log.NewWriter(f).F(log.DefFormatter), log.Op(log.Ge, "level", level), ) } log.Log = log.New(stderrBack, true).Domain(name).InfoLevel() if fileBack != nil { log.Log = log.New( log.NewMulti( stderrBack, log.DefFormatter, fileBack, log.DefFormatter, ), true, ).Domain(name).InfoLevel() } log.Println("Log Ok!") dns.SetLookupHostFunction(net.LookupHost) log.Println("Configuration...") cfgMail := cfg.Section("mail") smtpTimeout, err := cfgMail.Key("timeout").Int() if err != nil { log.Fatal("invalid smtp timeout") } server := cfgMail.Key("smtp").String() s := strings.Split(server, ":") if len(s) > 0 { server = s[0] } auth := smtp.PlainAuth( "", cfgMail.Key("account").String(), cfgMail.Key("password").String(), server, ) hostname := "your system" if hn, err := os.Hostname(); err == nil { hostname = hn } mons := make([]*monlite.Monitor, 0) for _, sec := range cfg.Sections() { if !strings.HasPrefix(sec.Name(), "service.") { continue } name := strings.TrimPrefix(sec.Name(), "service.") log.DebugLevel().Printf("Adding monitor %v for url: %v", name, sec.Key("url").String()) to, err := sec.Key("timeout").Int() if err != nil { log.Fatalf("invalid value in timeout for %v", name) } p, err := sec.Key("periode").Int() if err != nil { log.Fatalf("invalid value in timeout for %v", name) } sleep, err := sec.Key("sleep").Int() if err != nil { log.Fatalf("invalid value in sleep for %v", name) } fails, err := sec.Key("fails").Int() if err != nil { log.Fatalf("invalid value in fails for %v", name) } mons = append(mons, &monlite.Monitor{ Name: name, Url: sec.Key("url").String(), Timeout: time.Duration(to) * time.Second, Periode: time.Duration(p) * time.Second, Sleep: time.Duration(sleep) * time.Second, Fails: fails, OnFail: func(m *monlite.Monitor) error { body := "Mime-Version: 1.0\n" body += "Content-Type: text/plain; charset=utf-8\n" body += "From:" + cfgMail.Key("from").String() + "\n" body += "To:" + cfgMail.Key("to").String() + "\n" body += "Subject: [" + hostname + "] " + "Monitor fail for " + m.Name + "\n" body += "Hi! This is " + hostname + ".\n\n" body += "Monitor fail for " + m.Name + " " + m.Url + "\n\n" body += "Tank you, our lazy boy.\n" body += time.Now().Format(time.RFC1123Z) err := mysmtp.SendMail( cfgMail.Key("smtp").String(), auth, cfgMail.Key("from").String(), []string{cfgMail.Key("to").String()}, cfgMail.Key("helo").String(), []byte(body), time.Duration(smtpTimeout)*time.Second, false, ) if err != nil { return e.Forward(err) } return nil }, OnUnFail: func(m *monlite.Monitor) error { body := "Mime-Version: 1.0\n" body += "Content-Type: text/plain; charset=utf-8\n" body += "From:" + cfgMail.Key("from").String() + "\n" body += "To:" + cfgMail.Key("to").String() + "\n" body += "Subject: [" + hostname + "] " + "Monitor ok for " + m.Name + "\n" body += "Hi! This is " + hostname + ".\n\n" body += "Monitor ok for " + m.Name + " " + m.Url + "\n\n" body += "Tank you, our lazy boy.\n" body += time.Now().Format(time.RFC1123Z) err := mysmtp.SendMail( cfgMail.Key("smtp").String(), auth, cfgMail.Key("from").String(), []string{cfgMail.Key("to").String()}, cfgMail.Key("helo").String(), []byte(body), time.Duration(smtpTimeout)*time.Second, false, ) if err != nil { return e.Forward(err) } return nil }, }) } log.Println("Starting monitors...") for _, m := range mons { err := m.Start() if err != nil { log.Fatalf("Failed to start monitor for %v. Error: %v", m.Name, err) } } log.Println("Monitors ok!") sig := make(chan os.Signal) signal.Notify(sig, syscall.SIGINT, syscall.SIGKILL, syscall.SIGTERM) <-sig log.Println("Stop monitors...") for _, m := range mons { log.DebugLevel().Printf("Stop monitor %v", m.Name) err := m.Stop() if err != nil { log.Errorf("Failed to start monitor for %v. Error: %v", m.Name, err) } } log.Println("End.") }