package sircd import "github.com/sirupsen/logrus" import "github.com/spf13/viper" import "net" type ircd struct { Running bool serverName string log *logrus.Logger netName string ircPort uint16 httpPort uint16 ircListener net.Listener ircClients map[*ircClient]bool newClients chan *ircClient deadClients chan *ircClient messageQueue chan *ircMessage c *viper.Viper } const ( CONN_HOST = "localhost" CONN_PORT = "6667" CONN_TYPE = "tcp" ) func New(config *viper.Viper) *ircd { s := new(ircd) s.Running = true s.ircClients = make(map[*ircClient]bool) s.c = config s.serverName = s.c.GetString("myhostname") return s } func (s *ircd) SetLogger(logger *logrus.Logger) { s.log = logger } func (s *ircd) SetServerName(name string) { s.serverName = name } func (s *ircd) Start() { s.log.Infof("sircd version %s (%s) built %s by %s starting.", s.c.GetString("version"), s.c.GetString("buildarch"), s.c.GetString("buildtime"), s.c.GetString("builduser")) s.newClients = make(chan *ircClient, 128) s.deadClients = make(chan *ircClient, 128) s.messageQueue = make(chan *ircMessage, 128) var err error s.ircListener, err = net.Listen(CONN_TYPE, CONN_HOST+":"+CONN_PORT) if err != nil { s.log.Fatalln("Error listening:", err.Error()) } s.log.Println("Listening for irc proto on " + CONN_HOST + ":" + CONN_PORT) go func() { conn, err := s.ircListener.Accept() if err != nil { s.log.Panicf("Error accepting: ", err.Error()) } s.newClients <- newIrcClient(conn, s.log, s.messageQueue, s) }() // FIXME have this do 'go s.handlemessages()' and process an input channel // of messages from all clients in its own goroutine, and then call // directly into the main loop here and only do client i/o in this // goroutine go s.processMessages() s.mainLoop() } func (s *ircd) processMessages() { for { message := <-s.messageQueue s.processIRCMessage(message) } } func (s *ircd) mainLoop() { for { select { case client := <-s.newClients: s.ircClients[client] = true s.log.Println("new irc client: %s", client.Id) go func() { buf := make([]byte, 1024*1024) for { nbyte, err := client.conn.Read(buf) if err != nil { //this will surface it in the deadClients channel //reader below client.Kill() break } else { fragment := make([]byte, nbyte) copy(fragment, buf[:nbyte]) // this will populate the message channel client.AppendInputBuffer(fragment) } } }() case deadClient := <-s.deadClients: delete(s.ircClients, deadClient) deadClient.CleanupAndClose() } } }