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 {
hostname string
hostname *string
conn net.Conn
registered bool
nickname string
@ -49,7 +49,7 @@ func (client Client) String() 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: ""}
}
@ -89,7 +89,7 @@ func (client *Client) Msg(text string) {
// Send message from server. It has ": servername" prefix.
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

View File

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

View File

@ -25,7 +25,6 @@ import (
"regexp"
"sort"
"strings"
"sync"
"time"
)
@ -39,13 +38,12 @@ var (
RENickname = regexp.MustCompile("^[a-zA-Z0-9-]{1,9}$")
)
var passwordsRefreshLock sync.Mutex
type Daemon struct {
Verbose bool
version string
hostname string
motd string
hostname *string
motd *string
passwords *string
clients map[*Client]bool
clientAliveness map[*Client]*ClientAlivenessState
rooms map[string]*Room
@ -53,11 +51,10 @@ type Daemon struct {
lastAlivenessCheck time.Time
logSink chan<- LogEvent
stateSink chan<- StateEvent
passwords map[string]string
}
func NewDaemon(version, hostname, motd string, logSink chan<- LogEvent, stateSink chan<- StateEvent) *Daemon {
daemon := Daemon{version: version, hostname: hostname, motd: motd}
func NewDaemon(version string, hostname, motd, passwords *string, logSink chan<- LogEvent, stateSink chan<- StateEvent) *Daemon {
daemon := Daemon{version: version, hostname: hostname, motd: motd, passwords: passwords}
daemon.clients = make(map[*Client]bool)
daemon.clientAliveness = make(map[*Client]*ClientAlivenessState)
daemon.rooms = make(map[string]*Room)
@ -78,19 +75,19 @@ func (daemon *Daemon) SendLusers(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")
return
}
motd, err := ioutil.ReadFile(daemon.motd)
motd, err := ioutil.ReadFile(*daemon.motd)
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")
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") {
client.ReplyNicknamed("372", "- "+string(s))
}
@ -113,7 +110,7 @@ func (daemon *Daemon) SendWhois(client *Client, nicknames []string) {
h = "Unknown"
}
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{}
for _, room := range daemon.rooms {
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], ":")
}
if client.nickname != "*" && client.username != "" {
passwordsRefreshLock.Lock()
if daemon.passwords != nil && (client.password == "" || daemon.passwords[client.nickname] != client.password) {
passwordsRefreshLock.Unlock()
client.ReplyParts("462", "You may not register")
client.conn.Close()
return
if daemon.passwords != nil && *daemon.passwords != "" {
if client.password == "" {
client.ReplyParts("462", "You may not register")
client.conn.Close()
return
}
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
}
}
}
passwordsRefreshLock.Unlock()
client.registered = true
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("004", daemon.hostname+" goircd o o")
client.ReplyNicknamed("004", *daemon.hostname+" goircd o o")
daemon.SendLusers(client)
daemon.SendMotd(client)
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 c.registered {
c.Msg("PING :" + daemon.hostname)
c.Msg("PING :" + *daemon.hostname)
aliveness.pingSent = true
} else {
log.Println(c, "ping timeout")
@ -391,7 +403,7 @@ func (daemon *Daemon) Processor(events <-chan ClientEvent) {
client.ReplyNicknamed("409", "No origin specified")
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":
continue
case "NOTICE", "PRIVMSG":
@ -466,7 +478,7 @@ func (daemon *Daemon) Processor(events <-chan ClientEvent) {
} else {
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:
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) {
daemon := NewDaemon("ver1", "foohost", "", nil, nil)
host := "foohost"
daemon := NewDaemon("ver1", &host, nil, nil, nil, nil)
events := make(chan ClientEvent)
go daemon.Processor(events)
conn := NewTestingConn()
client := NewClient("foohost", conn)
client := NewClient(&host, conn)
go client.Processor(events)
conn.inbound <- "UNEXISTENT CMD" // should recieve nothing on this
@ -119,8 +120,10 @@ func TestMotd(t *testing.T) {
fd.WriteString("catched\n")
conn := NewTestingConn()
client := NewClient("foohost", conn)
daemon := NewDaemon("ver1", "foohost", fd.Name(), nil, nil)
host := "foohost"
client := NewClient(&host, conn)
motdName := fd.Name()
daemon := NewDaemon("ver1", &host, &motdName, nil, nil, nil)
daemon.SendMotd(client)
if r := <-conn.outbound; !strings.HasPrefix(r, ":foohost 375") {

View File

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

10
room.go
View File

@ -41,16 +41,16 @@ type Room struct {
topic string
key string
members map[*Client]bool
hostname string
hostname *string
logSink chan<- LogEvent
stateSink chan<- StateEvent
}
func (r Room) String() string {
return r.name
func (room Room) String() string {
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.members = make(map[*Client]bool)
room.topic = ""
@ -128,7 +128,7 @@ func (room *Room) Processor(events <-chan ClientEvent) {
room.StateSave()
case EventWho:
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")
case EventMode:

View File

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