Remove signal processor for password reloading and use pointers to strings

Simplify code and server maintaining by removing password file caching
and refreshing it with HUP signal. Also use pointers to strings, already
presented after flag-module work.
This commit is contained in:
Sergey Matveev 2014-08-19 18:11:56 +04:00
parent 85e6538f2f
commit 990937d451
7 changed files with 66 additions and 82 deletions

View File

@ -31,7 +31,7 @@ const (
) )
type Client struct { type Client struct {
hostname string hostname *string
conn net.Conn conn net.Conn
registered bool registered bool
nickname string nickname string
@ -49,7 +49,7 @@ func (client Client) String() string {
return client.nickname + "!" + client.username + "@" + client.conn.RemoteAddr().String() return client.nickname + "!" + client.username + "@" + client.conn.RemoteAddr().String()
} }
func NewClient(hostname string, conn net.Conn) *Client { func NewClient(hostname *string, conn net.Conn) *Client {
return &Client{hostname: hostname, conn: conn, nickname: "*", password: ""} return &Client{hostname: hostname, conn: conn, nickname: "*", password: ""}
} }
@ -89,7 +89,7 @@ func (client *Client) Msg(text string) {
// Send message from server. It has ": servername" prefix. // Send message from server. It has ": servername" prefix.
func (client *Client) Reply(text string) { func (client *Client) Reply(text string) {
client.Msg(":" + client.hostname + " " + text) client.Msg(":" + *client.hostname + " " + text)
} }
// Send server message, concatenating all provided text parts and // Send server message, concatenating all provided text parts and

View File

@ -96,7 +96,8 @@ func (conn TestingConn) SetWriteDeadline(t time.Time) error {
func TestNewClient(t *testing.T) { func TestNewClient(t *testing.T) {
conn := NewTestingConn() conn := NewTestingConn()
sink := make(chan ClientEvent) sink := make(chan ClientEvent)
client := NewClient("foohost", conn) host := "foohost"
client := NewClient(&host, conn)
go client.Processor(sink) go client.Processor(sink)
event := <-sink event := <-sink
@ -123,7 +124,8 @@ func TestNewClient(t *testing.T) {
// Test replies formatting // Test replies formatting
func TestClientReplies(t *testing.T) { func TestClientReplies(t *testing.T) {
conn := NewTestingConn() conn := NewTestingConn()
client := NewClient("foohost", conn) host := "foohost"
client := NewClient(&host, conn)
client.nickname = "мойник" client.nickname = "мойник"
client.Reply("hello") client.Reply("hello")

View File

@ -25,7 +25,6 @@ import (
"regexp" "regexp"
"sort" "sort"
"strings" "strings"
"sync"
"time" "time"
) )
@ -39,13 +38,12 @@ var (
RENickname = regexp.MustCompile("^[a-zA-Z0-9-]{1,9}$") RENickname = regexp.MustCompile("^[a-zA-Z0-9-]{1,9}$")
) )
var passwordsRefreshLock sync.Mutex
type Daemon struct { type Daemon struct {
Verbose bool Verbose bool
version string version string
hostname string hostname *string
motd string motd *string
passwords *string
clients map[*Client]bool clients map[*Client]bool
clientAliveness map[*Client]*ClientAlivenessState clientAliveness map[*Client]*ClientAlivenessState
rooms map[string]*Room rooms map[string]*Room
@ -53,11 +51,10 @@ type Daemon struct {
lastAlivenessCheck time.Time lastAlivenessCheck time.Time
logSink chan<- LogEvent logSink chan<- LogEvent
stateSink chan<- StateEvent stateSink chan<- StateEvent
passwords map[string]string
} }
func NewDaemon(version, hostname, motd string, logSink chan<- LogEvent, stateSink chan<- StateEvent) *Daemon { func NewDaemon(version string, hostname, motd, passwords *string, logSink chan<- LogEvent, stateSink chan<- StateEvent) *Daemon {
daemon := Daemon{version: version, hostname: hostname, motd: motd} daemon := Daemon{version: version, hostname: hostname, motd: motd, passwords: passwords}
daemon.clients = make(map[*Client]bool) daemon.clients = make(map[*Client]bool)
daemon.clientAliveness = make(map[*Client]*ClientAlivenessState) daemon.clientAliveness = make(map[*Client]*ClientAlivenessState)
daemon.rooms = make(map[string]*Room) daemon.rooms = make(map[string]*Room)
@ -78,19 +75,19 @@ func (daemon *Daemon) SendLusers(client *Client) {
} }
func (daemon *Daemon) SendMotd(client *Client) { func (daemon *Daemon) SendMotd(client *Client) {
if len(daemon.motd) == 0 { if daemon.motd == nil || *daemon.motd == "" {
client.ReplyNicknamed("422", "MOTD File is missing") client.ReplyNicknamed("422", "MOTD File is missing")
return return
} }
motd, err := ioutil.ReadFile(daemon.motd) motd, err := ioutil.ReadFile(*daemon.motd)
if err != nil { if err != nil {
log.Printf("Can not read motd file %s: %v", daemon.motd, err) log.Printf("Can not read motd file %s: %v", *daemon.motd, err)
client.ReplyNicknamed("422", "Error reading MOTD File") client.ReplyNicknamed("422", "Error reading MOTD File")
return return
} }
client.ReplyNicknamed("375", "- "+daemon.hostname+" Message of the day -") client.ReplyNicknamed("375", "- "+*daemon.hostname+" Message of the day -")
for _, s := range strings.Split(strings.Trim(string(motd), "\n"), "\n") { for _, s := range strings.Split(strings.Trim(string(motd), "\n"), "\n") {
client.ReplyNicknamed("372", "- "+string(s)) client.ReplyNicknamed("372", "- "+string(s))
} }
@ -113,7 +110,7 @@ func (daemon *Daemon) SendWhois(client *Client, nicknames []string) {
h = "Unknown" h = "Unknown"
} }
client.ReplyNicknamed("311", c.nickname, c.username, h, "*", c.realname) client.ReplyNicknamed("311", c.nickname, c.username, h, "*", c.realname)
client.ReplyNicknamed("312", c.nickname, daemon.hostname, daemon.hostname) client.ReplyNicknamed("312", c.nickname, *daemon.hostname, *daemon.hostname)
subscriptions := []string{} subscriptions := []string{}
for _, room := range daemon.rooms { for _, room := range daemon.rooms {
for subscriber := range room.members { for subscriber := range room.members {
@ -196,19 +193,34 @@ func (daemon *Daemon) ClientRegister(client *Client, command string, cols []stri
client.realname = strings.TrimLeft(args[3], ":") client.realname = strings.TrimLeft(args[3], ":")
} }
if client.nickname != "*" && client.username != "" { if client.nickname != "*" && client.username != "" {
passwordsRefreshLock.Lock() if daemon.passwords != nil && *daemon.passwords != "" {
if daemon.passwords != nil && (client.password == "" || daemon.passwords[client.nickname] != client.password) { if client.password == "" {
passwordsRefreshLock.Unlock()
client.ReplyParts("462", "You may not register") client.ReplyParts("462", "You may not register")
client.conn.Close() client.conn.Close()
return return
} }
passwordsRefreshLock.Unlock() contents, err := ioutil.ReadFile(*daemon.passwords)
if err != nil {
log.Fatalf("Can no read passwords file %s: %s", *daemon.passwords, err)
return
}
for _, entry := range strings.Split(string(contents), "\n") {
if entry == "" {
continue
}
if lp := strings.Split(entry, ":"); lp[0] == client.nickname && lp[1] != client.password {
client.ReplyParts("462", "You may not register")
client.conn.Close()
return
}
}
}
client.registered = true client.registered = true
client.ReplyNicknamed("001", "Hi, welcome to IRC") client.ReplyNicknamed("001", "Hi, welcome to IRC")
client.ReplyNicknamed("002", "Your host is "+daemon.hostname+", running goircd "+daemon.version) client.ReplyNicknamed("002", "Your host is "+*daemon.hostname+", running goircd "+daemon.version)
client.ReplyNicknamed("003", "This server was created sometime") client.ReplyNicknamed("003", "This server was created sometime")
client.ReplyNicknamed("004", daemon.hostname+" goircd o o") client.ReplyNicknamed("004", *daemon.hostname+" goircd o o")
daemon.SendLusers(client) daemon.SendLusers(client)
daemon.SendMotd(client) daemon.SendMotd(client)
log.Println(client, "logged in") log.Println(client, "logged in")
@ -295,7 +307,7 @@ func (daemon *Daemon) Processor(events <-chan ClientEvent) {
} }
if !aliveness.pingSent && aliveness.timestamp.Add(PingThreshold).Before(now) { if !aliveness.pingSent && aliveness.timestamp.Add(PingThreshold).Before(now) {
if c.registered { if c.registered {
c.Msg("PING :" + daemon.hostname) c.Msg("PING :" + *daemon.hostname)
aliveness.pingSent = true aliveness.pingSent = true
} else { } else {
log.Println(c, "ping timeout") log.Println(c, "ping timeout")
@ -391,7 +403,7 @@ func (daemon *Daemon) Processor(events <-chan ClientEvent) {
client.ReplyNicknamed("409", "No origin specified") client.ReplyNicknamed("409", "No origin specified")
continue continue
} }
client.Reply(fmt.Sprintf("PONG %s :%s", daemon.hostname, cols[1])) client.Reply(fmt.Sprintf("PONG %s :%s", *daemon.hostname, cols[1]))
case "PONG": case "PONG":
continue continue
case "NOTICE", "PRIVMSG": case "NOTICE", "PRIVMSG":
@ -466,7 +478,7 @@ func (daemon *Daemon) Processor(events <-chan ClientEvent) {
} else { } else {
debug = "" debug = ""
} }
client.ReplyNicknamed("351", fmt.Sprintf("%s.%s %s :", daemon.version, debug, daemon.hostname)) client.ReplyNicknamed("351", fmt.Sprintf("%s.%s %s :", daemon.version, debug, *daemon.hostname))
default: default:
client.ReplyNicknamed("421", command, "Unknown command") client.ReplyNicknamed("421", command, "Unknown command")
} }
@ -477,22 +489,3 @@ func (daemon *Daemon) Processor(events <-chan ClientEvent) {
} }
} }
} }
func (daemon *Daemon) PasswordsRefresh() {
contents, err := ioutil.ReadFile(*passwords)
if err != nil {
log.Fatalf("Can no read passwords file %s: %s", *passwords, err)
return
}
processed := make(map[string]string)
for _, entry := range strings.Split(string(contents), "\n") {
loginAndPassword := strings.Split(entry, ":")
if len(loginAndPassword) == 2 {
processed[loginAndPassword[0]] = loginAndPassword[1]
}
}
log.Printf("Read %d passwords", len(processed))
passwordsRefreshLock.Lock()
daemon.passwords = processed
passwordsRefreshLock.Unlock()
}

View File

@ -25,11 +25,12 @@ import (
) )
func TestRegistrationWorkflow(t *testing.T) { func TestRegistrationWorkflow(t *testing.T) {
daemon := NewDaemon("ver1", "foohost", "", nil, nil) host := "foohost"
daemon := NewDaemon("ver1", &host, nil, nil, nil, nil)
events := make(chan ClientEvent) events := make(chan ClientEvent)
go daemon.Processor(events) go daemon.Processor(events)
conn := NewTestingConn() conn := NewTestingConn()
client := NewClient("foohost", conn) client := NewClient(&host, conn)
go client.Processor(events) go client.Processor(events)
conn.inbound <- "UNEXISTENT CMD" // should recieve nothing on this conn.inbound <- "UNEXISTENT CMD" // should recieve nothing on this
@ -119,8 +120,10 @@ func TestMotd(t *testing.T) {
fd.WriteString("catched\n") fd.WriteString("catched\n")
conn := NewTestingConn() conn := NewTestingConn()
client := NewClient("foohost", conn) host := "foohost"
daemon := NewDaemon("ver1", "foohost", fd.Name(), nil, nil) client := NewClient(&host, conn)
motdName := fd.Name()
daemon := NewDaemon("ver1", &host, &motdName, nil, nil, nil)
daemon.SendMotd(client) daemon.SendMotd(client)
if r := <-conn.outbound; !strings.HasPrefix(r, ":foohost 375") { if r := <-conn.outbound; !strings.HasPrefix(r, ":foohost 375") {

View File

@ -23,12 +23,9 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"net" "net"
"os"
"os/signal"
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall"
) )
var ( var (
@ -53,7 +50,7 @@ func listenerLoop(sock net.Listener, events chan<- ClientEvent) {
log.Println("Error during accepting connection", err) log.Println("Error during accepting connection", err)
continue continue
} }
client := NewClient(*hostname, conn) client := NewClient(hostname, conn)
go client.Processor(events) go client.Processor(events)
} }
} }
@ -79,9 +76,9 @@ func Run() {
} }
stateSink := make(chan StateEvent) stateSink := make(chan StateEvent)
daemon := NewDaemon(version, *hostname, *motd, logSink, stateSink) daemon := NewDaemon(version, hostname, motd, passwords, logSink, stateSink)
daemon.Verbose = *verbose daemon.Verbose = *verbose
log.Println("goircd "+daemon.version+" is starting") log.Println("goircd " + daemon.version + " is starting")
if *statedir == "" { if *statedir == "" {
// Dummy statekeeper // Dummy statekeeper
go func() { go func() {
@ -115,19 +112,6 @@ func Run() {
log.Println(*statedir, "statekeeper initialized") log.Println(*statedir, "statekeeper initialized")
} }
if *passwords != "" {
daemon.PasswordsRefresh()
hups := make(chan os.Signal)
signal.Notify(hups, syscall.SIGHUP)
go func() {
for {
<-hups
daemon.PasswordsRefresh()
}
}()
}
if *bind != "" { if *bind != "" {
listener, err := net.Listen("tcp", *bind) listener, err := net.Listen("tcp", *bind)
if err != nil { if err != nil {

10
room.go
View File

@ -41,16 +41,16 @@ type Room struct {
topic string topic string
key string key string
members map[*Client]bool members map[*Client]bool
hostname string hostname *string
logSink chan<- LogEvent logSink chan<- LogEvent
stateSink chan<- StateEvent stateSink chan<- StateEvent
} }
func (r Room) String() string { func (room Room) String() string {
return r.name return room.name
} }
func NewRoom(hostname, name string, logSink chan<- LogEvent, stateSink chan<- StateEvent) *Room { func NewRoom(hostname *string, name string, logSink chan<- LogEvent, stateSink chan<- StateEvent) *Room {
room := Room{name: name} room := Room{name: name}
room.members = make(map[*Client]bool) room.members = make(map[*Client]bool)
room.topic = "" room.topic = ""
@ -128,7 +128,7 @@ func (room *Room) Processor(events <-chan ClientEvent) {
room.StateSave() room.StateSave()
case EventWho: case EventWho:
for m := range room.members { for m := range room.members {
client.ReplyNicknamed("352", room.name, m.username, m.conn.RemoteAddr().String(), room.hostname, m.nickname, "H", "0 "+m.realname) client.ReplyNicknamed("352", room.name, m.username, m.conn.RemoteAddr().String(), *room.hostname, m.nickname, "H", "0 "+m.realname)
} }
client.ReplyNicknamed("315", room.name, "End of /WHO list") client.ReplyNicknamed("315", room.name, "End of /WHO list")
case EventMode: case EventMode:

View File

@ -26,14 +26,15 @@ func notEnoughParams(t *testing.T, c *TestingConn) {
func TestTwoUsers(t *testing.T) { func TestTwoUsers(t *testing.T) {
logSink := make(chan LogEvent, 8) logSink := make(chan LogEvent, 8)
stateSink := make(chan StateEvent, 8) stateSink := make(chan StateEvent, 8)
daemon := NewDaemon("ver1", "foohost", "", logSink, stateSink) host := "foohost"
daemon := NewDaemon("ver1", &host, nil, nil, logSink, stateSink)
events := make(chan ClientEvent) events := make(chan ClientEvent)
go daemon.Processor(events) go daemon.Processor(events)
conn1 := NewTestingConn() conn1 := NewTestingConn()
conn2 := NewTestingConn() conn2 := NewTestingConn()
client1 := NewClient("foohost", conn1) client1 := NewClient(&host, conn1)
client2 := NewClient("foohost", conn2) client2 := NewClient(&host, conn2)
go client1.Processor(events) go client1.Processor(events)
go client2.Processor(events) go client2.Processor(events)
@ -100,11 +101,12 @@ func TestTwoUsers(t *testing.T) {
func TestJoin(t *testing.T) { func TestJoin(t *testing.T) {
logSink := make(chan LogEvent, 8) logSink := make(chan LogEvent, 8)
stateSink := make(chan StateEvent, 8) stateSink := make(chan StateEvent, 8)
daemon := NewDaemon("ver1", "foohost", "", logSink, stateSink) host := "foohost"
daemon := NewDaemon("ver1", &host, nil, nil, logSink, stateSink)
events := make(chan ClientEvent) events := make(chan ClientEvent)
go daemon.Processor(events) go daemon.Processor(events)
conn := NewTestingConn() conn := NewTestingConn()
client := NewClient("foohost", conn) client := NewClient(&host, conn)
go client.Processor(events) go client.Processor(events)
conn.inbound <- "NICK nick2\r\nUSER foo2 bar2 baz2 :Long name2\r\n" conn.inbound <- "NICK nick2\r\nUSER foo2 bar2 baz2 :Long name2\r\n"