From 2c1db1a57392a1e0ec84b1563b73c05864323d5e Mon Sep 17 00:00:00 2001 From: Andreas Neue Date: Tue, 19 Jul 2016 18:25:39 +0200 Subject: [PATCH] Added basic monitoring; channel topics --- client.go | 23 ++--- monitoring.go | 40 ++++++++ server.go | 251 +++++++++++++++++++++----------------------------- 3 files changed, 152 insertions(+), 162 deletions(-) create mode 100644 monitoring.go diff --git a/client.go b/client.go index 345f6b9..2f4141a 100644 --- a/client.go +++ b/client.go @@ -18,7 +18,7 @@ type Client interface { Name() string Send(*irc.Message) Receive(*irc.Message) - Register() chan bool + //Register() chan bool Destroy() } @@ -50,7 +50,7 @@ func NewRemoteClient(srv *Server, conn net.Conn) *RemoteClient { cl.modes = "" cl.receive = make(chan *irc.Message) - cl.register = make(chan bool) + //cl.register = make(chan bool) cl.isRegistered = false cl.isAuthed = false @@ -80,10 +80,6 @@ func (cl *RemoteClient) Receive(msg *irc.Message) { cl.receive <- msg } -func (cl *RemoteClient) Register() chan bool { - return cl.register -} - func (cl *RemoteClient) AddMode(mode string) { cl.modes = cl.modes + mode } @@ -212,19 +208,14 @@ func handleLineNick(cl *RemoteClient, msg *irc.Message) bool { if cl.name != "" { return false } - if len(msg.Args) < 1 { - xlog.Warning("Nicksalat!") + cl.name = msg.Args[0] + if _, exists := cl.server.clients[cl.name]; exists { + cl.Destroy() + xlog.Error("Registration of user '%s' failed", msg.Args[0]) return false } - cl.name = msg.Args[0] cl.server.AddClient <- cl - if registered := <-cl.register; registered { - cl.isRegistered = true - xlog.Info("User '%s' registered", msg.Args[0]) - } else { - xlog.Error("User '%s' already connected", msg.Args[0]) - cl.server.DelClient <- cl - } + xlog.Info("User '%s' registered", msg.Args[0]) return false } diff --git a/monitoring.go b/monitoring.go new file mode 100644 index 0000000..6d356dd --- /dev/null +++ b/monitoring.go @@ -0,0 +1,40 @@ +// vim:ts=4:sts=4:sw=4:noet:tw=72 + +package ircd + +import ( + "net/http" + "time" + + "github.com/prometheus/client_golang/prometheus" +) + +var ( + gaugePacketsTransferred prometheus.Gauge + gaugeClientConnections prometheus.Gauge +) + +func monitoringRun(srv *Server) { + gaugePacketsTransferred = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "ircd_packets_transferred", + Help: "Packets handled", + }) + gaugeClientConnections = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "ircd_clients_connected", + Help: "Client connections", + }) + prometheus.MustRegister(gaugePacketsTransferred) + prometheus.MustRegister(gaugeClientConnections) + go monitoringUpdater(srv) + http.Handle("/metrics", prometheus.Handler()) + laddr, _ := srv.config.GetString("net", "listen_prom") + http.ListenAndServe(laddr, nil) +} + +func monitoringUpdater(srv *Server) { + for { + time.Sleep(5 * time.Second) + gaugePacketsTransferred.Set(srv.packetsTransferred) + gaugeClientConnections.Set(srv.clientConnections) + } +} diff --git a/server.go b/server.go index de08b5e..5b5b666 100644 --- a/server.go +++ b/server.go @@ -7,7 +7,6 @@ import ( "net" "os" "runtime" - "strconv" "strings" "time" @@ -37,10 +36,14 @@ type Server struct { created string motd string clients map[string]Client - channels map[string]map[string]string + chSubs map[string]map[string]string + chTopics map[string]string ports map[int]bool config *conf.ConfigFile configPath string + + packetsTransferred float64 + clientConnections float64 } func init() { @@ -55,7 +58,8 @@ func NewServer(configPath, software, version string) *Server { srv.DelClient = make(chan Client, 1024) srv.clients = make(map[string]Client) - srv.channels = make(map[string]map[string]string) + srv.chSubs = make(map[string]map[string]string) + srv.chTopics = make(map[string]string) srv.configPath = configPath srv.loadConfig() loglevel, _ := srv.config.GetInt("system", "loglevel") @@ -64,38 +68,38 @@ func NewServer(configPath, software, version string) *Server { srv.motd, _ = srv.config.GetString("server", "motd") xlog.Init(loglevel) + srv.packetsTransferred = 0 + srv.clientConnections = 0 + return srv } // Open the listening port and start the main server loop. func (srv *Server) Run() { xlog.Info("%s/%s", srv.software, srv.version) - address, _ := srv.config.GetString("net", "address") - port, _ := srv.config.GetInt("net", "port") - go srv.listen(address, port) + go monitoringRun(srv) + laddr, _ := srv.config.GetString("net", "listen_ircd") + go srv.listen(laddr) srv.dispatch() } -func (srv *Server) listen(address string, port int) { - if _, exists := srv.ports[port]; exists { - xlog.Warning("Port %i already opened", port) - } - listen, err := net.Listen("tcp", address+":"+strconv.Itoa(port)) +func (srv *Server) listen(laddr string) { + listen, err := net.Listen("tcp", laddr) if err != nil { - xlog.Fatal("Cannot listen on port %i (%s), exiting", - port, err.Error()) + xlog.Fatal(err.Error()) os.Exit(-1) } - xlog.Info("Start listening on port %d", port) + xlog.Info("Start listening on %s", laddr) for { - time.Sleep(1e6) + time.Sleep(1 * time.Millisecond) conn, err := listen.Accept() if err != nil { xlog.Error(err.Error()) } else { NewRemoteClient(srv, conn) + srv.clientConnections++ } } } @@ -107,27 +111,19 @@ func (srv *Server) dispatch() { select { case msg := <-srv.Dispatch: srv.recvMsg(msg) + srv.packetsTransferred++ case cl := <-srv.AddClient: name := cl.Name() - if _, exists := srv.clients[name]; exists { - xlog.Warning("Client registration failed: '%s'", name) - go func() { cl.Register() <- false }() - continue - } - go func() { cl.Register() <- true }() srv.clients[name] = cl + srv.clientLogon(cl) + srv.clientMotd(cl) xlog.Info("Client registered: '%s'", name) xlog.Info("Server has %d client(s)", len(srv.clients)) xlog.Debug("Goroutines running: %d", runtime.NumGoroutine()) - srv.clientLogon(cl) - srv.clientMotd(cl) case cl := <-srv.DelClient: name := cl.Name() cl.Destroy() - //go func() { - // time.Sleep(10 * time.Second) delete(srv.clients, name) - //}() xlog.Info("Client deleted: '%s'", name) xlog.Info("Server has %d client(s)", len(srv.clients)) xlog.Debug("Goroutines running: %d", runtime.NumGoroutine()) @@ -157,41 +153,45 @@ func (srv *Server) recvMsg(msg *irc.Message) { srv.sendReply(msg.Pre, ERR_NEEDMOREPARAMS, cmd, "Not enough parameters") return } + if hook.NeedTrail && msg.Trail == "" { + srv.sendReply(msg.Pre, ERR_NEEDMOREPARAMS, cmd, "Not enough parameters") + return + } hook.HookFn(srv, msg) } func (srv *Server) sendMsg(msg *irc.Message) { if strings.HasPrefix(msg.Args[0], "#") { - srv.channelBroadcast(msg.Args[0], msg) + srv.bcChan(msg) } else { - srv.sendMsgToClient(msg) + srv.sendClient(msg) } } -func (srv *Server) sendMsgToChannel(msg *irc.Message) { - cname := msg.Args[0] - if _, exists := srv.channels[cname]; !exists { - return - } - subs := srv.channels[cname] - for sub, _ := range subs { - if _, exists := srv.clients[sub]; !exists { - continue - } - cl := srv.clients[sub] - cl.Receive(msg) - } -} - -func (srv *Server) sendMsgToClient(msg *irc.Message) { +func (srv *Server) sendClient(msg *irc.Message) { if _, exists := srv.clients[msg.Args[0]]; !exists { - xlog.Error("sendMsgToClient: Client '%s' does not exist", msg.Args[0]) + xlog.Debug("sendClient: Client '%s' does not exist", msg.Args[0]) + srv.sendReply(msg.Pre, ERR_NOSUCHNICK, msg.Args[0], "No such nick/channel") return } cl := srv.clients[msg.Args[0]] cl.Receive(msg) } +func (srv *Server) bcChan(msg *irc.Message) { + ch := msg.Args[0] + if _, exists := srv.chSubs[ch]; !exists { + xlog.Error("bcChan: Channel '%s' does not exist", ch) + return + } + for nick, _ := range srv.chSubs[ch] { + if msg.Pre == nick && msg.Cmd == "PRIVMSG" { + continue + } + srv.clients[nick].Receive(msg) + } +} + func (srv *Server) sendReply(tar, cmd, args, trail string) { if _, exists := srv.clients[tar]; !exists { xlog.Error("sendReply: Client '%s' does not exist", tar) @@ -199,7 +199,7 @@ func (srv *Server) sendReply(tar, cmd, args, trail string) { } cl := srv.clients[tar] if args != "" { - args = tar + args + args = tar + " " + args } else { args = tar } @@ -228,71 +228,68 @@ func (srv *Server) clientMotd(cl Client) { srv.sendReply(cl.Name(), RPL_ENDOFMOTD, "", "End of MOTD command") } -func (srv *Server) channelBroadcast(ch string, msg *irc.Message) { - for nick, _ := range srv.channels[ch] { - if msg.Pre == nick && msg.Cmd == "PRIVMSG" { - continue - } - srv.clients[nick].Receive(msg) - } -} - func (srv *Server) channelJoin(nick, ch string) { - if _, exists := srv.channels[ch]; !exists { - srv.channels[ch] = make(map[string]string) + if _, exists := srv.chSubs[ch]; !exists { + srv.chSubs[ch] = make(map[string]string) + srv.chTopics[ch] = "" } - if _, exists := srv.channels[ch][nick]; exists { + if _, exists := srv.chSubs[ch][nick]; exists { return } - srv.channels[ch][nick] = "" - srv.channelBroadcast(ch, irc.M(nick, "JOIN", ch, "")) + srv.chSubs[ch][nick] = "" + srv.bcChan(irc.M(nick, "JOIN", ch, "")) + srv.sendReply(nick, RPL_TOPIC, ch, srv.chTopics[ch]) } func (srv *Server) channelPart(nick, ch, reason string) { - if _, exists := srv.channels[ch]; !exists { + if _, exists := srv.chSubs[ch]; !exists { return } - if _, exists := srv.channels[ch][nick]; !exists { + if _, exists := srv.chSubs[ch][nick]; !exists { return } - srv.channelBroadcast(ch, irc.M(nick, "PART", ch, reason)) - delete(srv.channels[ch], nick) + srv.bcChan(irc.M(nick, "PART", ch, reason)) + delete(srv.chSubs[ch], nick) } type SrvCommandHook struct { - HookFn func(srv *Server, msg *irc.Message) - MinArgs int - NeedOper bool - NeedAuth bool + HookFn func(srv *Server, msg *irc.Message) + MinArgs int + NeedTrail bool + NeedOper bool + NeedAuth bool } var srvCommandHooks = map[string]SrvCommandHook{ - "PRIVMSG": {srvHandleCmdPrivmsg, 1, false, false}, - "JOIN": {srvHandleCmdJoin, 1, false, false}, - "PART": {srvHandleCmdPart, 1, false, false}, - "QUIT": {srvHandleCmdQuit, 0, false, false}, - "MODE": {srvHandleCmdMode, 1, false, false}, - "LIST": {srvHandleCmdList, 0, false, false}, - "VERSION": {srvHandleCmdVersion, 0, false, false}, - "STATS": {srvHandleCmdStats, 0, false, false}, - "TIME": {srvHandleCmdTime, 0, false, false}, - "OPER": {srvHandleCmdOper, 1, false, false}, - "ADMIN": {srvHandleCmdAdmin, 0, false, false}, - "INFO": {srvHandleCmdInfo, 0, false, false}, - "WHO": {srvHandleCmdWho, 0, false, false}, - "WHOIS": {srvHandleCmdWhois, 0, false, false}, - "WHOWAS": {srvHandleCmdWhowas, 0, false, false}, - "KILL": {srvHandleCmdKill, 0, false, false}, - "PING": {srvHandleCmdPing, 0, false, false}, - "PONG": {srvHandleCmdPong, 0, false, false}, - "ERROR": {srvHandleCmdError, 0, false, false}, - "AWAY": {srvHandleCmdAway, 0, false, false}, - "REHASH": {srvHandleCmdRehash, 0, false, false}, - "RESTART": {srvHandleCmdRestart, 0, false, false}, - "SUMMON": {srvHandleCmdSummon, 0, false, false}, - "USERS": {srvHandleCmdUsers, 0, false, false}, - "USERHOST": {srvHandleCmdUserhost, 0, false, false}, - "ISON": {srvHandleCmdIson, 0, false, false}, + "PRIVMSG": {srvHandleCmdPrivmsg, 1, true, false, false}, + "JOIN": {srvHandleCmdJoin, 1, false, false, false}, + "PART": {srvHandleCmdPart, 1, false, false, false}, + "QUIT": {srvHandleCmdQuit, 0, false, false, false}, + "MODE": {srvHandleCmdMode, 1, false, false, false}, + "TOPIC": {srvHandleCmdTopic, 1, false, false, false}, + "WHOIS": {srvHandleCmdWhois, 0, false, false, false}, + "PING": {srvHandleCmdPing, 1, false, false, false}, + "REHASH": {srvHandleCmdRehash, 0, false, false, false}, + /* + "LIST": {srvHandleCmdList, 0, false, false}, + "VERSION": {srvHandleCmdVersion, 0, false, false}, + "STATS": {srvHandleCmdStats, 0, false, false}, + "TIME": {srvHandleCmdTime, 0, false, false}, + "OPER": {srvHandleCmdOper, 1, false, false}, + "ADMIN": {srvHandleCmdAdmin, 0, false, false}, + "INFO": {srvHandleCmdInfo, 0, false, false}, + "WHO": {srvHandleCmdWho, 0, false, false}, + "WHOWAS": {srvHandleCmdWhowas, 0, false, false}, + "KILL": {srvHandleCmdKill, 0, false, false}, + "PONG": {srvHandleCmdPong, 0, false, false}, + "ERROR": {srvHandleCmdError, 0, false, false}, + "AWAY": {srvHandleCmdAway, 0, false, false}, + "RESTART": {srvHandleCmdRestart, 0, false, false}, + "SUMMON": {srvHandleCmdSummon, 0, false, false}, + "USERS": {srvHandleCmdUsers, 0, false, false}, + "USERHOST": {srvHandleCmdUserhost, 0, false, false}, + "ISON": {srvHandleCmdIson, 0, false, false}, + */ } func srvHandleCmdPrivmsg(srv *Server, msg *irc.Message) { @@ -307,76 +304,38 @@ func srvHandleCmdPart(srv *Server, msg *irc.Message) { srv.channelPart(msg.Pre, msg.Args[0], msg.Trail) } -func srvHandleCmdOper(srv *Server, msg *irc.Message) { -} - func srvHandleCmdQuit(srv *Server, msg *irc.Message) { + } func srvHandleCmdMode(srv *Server, msg *irc.Message) { + } -func srvHandleCmdList(srv *Server, msg *irc.Message) { -} - -func srvHandleCmdVersion(srv *Server, msg *irc.Message) { -} - -func srvHandleCmdStats(srv *Server, msg *irc.Message) { -} - -func srvHandleCmdTime(srv *Server, msg *irc.Message) { -} - -func srvHandleCmdAdmin(srv *Server, msg *irc.Message) { -} - -func srvHandleCmdInfo(srv *Server, msg *irc.Message) { -} - -func srvHandleCmdWho(srv *Server, msg *irc.Message) { +func srvHandleCmdTopic(srv *Server, msg *irc.Message) { + ch := msg.Args[0] + if _, exists := srv.chSubs[ch]; !exists { + srv.sendReply(msg.Pre, ERR_NOSUCHCHANNEL, ch, "No such channel") + } + if msg.Trail == "" { + srv.sendReply(msg.Pre, RPL_TOPIC, ch, srv.chTopics[ch]) + } else { + srv.chTopics[ch] = msg.Trail + srv.bcChan(msg) + //srv.sendReply(msg.Pre, RPL_TOPIC, ch, msg.Trail) + } } func srvHandleCmdWhois(srv *Server, msg *irc.Message) { srv.sendReply(msg.Pre, RPL_WHOISUSER, "nick user host *", "real name") } -func srvHandleCmdWhowas(srv *Server, msg *irc.Message) { -} - -func srvHandleCmdKill(srv *Server, msg *irc.Message) { -} - func srvHandleCmdPing(srv *Server, msg *irc.Message) { srv.sendReply(msg.Pre, "PONG", msg.Args[0], "") } -func srvHandleCmdPong(srv *Server, msg *irc.Message) { -} - -func srvHandleCmdError(srv *Server, msg *irc.Message) { -} - -func srvHandleCmdAway(srv *Server, msg *irc.Message) { -} - func srvHandleCmdRehash(srv *Server, msg *irc.Message) { srv.loadConfig() srv.sendReply(msg.Pre, RPL_REHASHING, "", "Rehashing.") xlog.Info("Rehashing") } - -func srvHandleCmdRestart(srv *Server, msg *irc.Message) { -} - -func srvHandleCmdSummon(srv *Server, msg *irc.Message) { -} - -func srvHandleCmdUsers(srv *Server, msg *irc.Message) { -} - -func srvHandleCmdUserhost(srv *Server, msg *irc.Message) { -} - -func srvHandleCmdIson(srv *Server, msg *irc.Message) { -}