aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hid/main.go460
1 files changed, 253 insertions, 207 deletions
diff --git a/hid/main.go b/hid/main.go
index 72a6df0..98a8b3b 100644
--- a/hid/main.go
+++ b/hid/main.go
@@ -16,55 +16,101 @@
// hid is a straight-forward port of kike IRCd from C.
package main
-/*
+import (
+ "bufio"
+ "crypto/sha256"
+ "crypto/tls"
+ "encoding/hex"
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "os"
+ "os/signal"
+ "os/user"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+ "syscall"
+ "time"
+)
-// ANSI terminal formatting, would be better if we had isatty() available
-func tf(text string, ansi string) string {
- return "\x1b[0;" + ansi + "m" + text + "\x1b[0m"
-}
+var debugMode = false
-func logErrorf(format string, args ...interface{}) {
- fmt.Fprintf(os.Stderr, tf("error: "+format+"\n", "1;31"), args...)
-}
+const (
+ projectName = "hid"
+ // TODO: Consider using the same version number for all subprojects.
+ projectVersion = "0"
+)
-func logFatalf(format string, args ...interface{}) {
- fmt.Fprintf(os.Stderr, tf("fatal: "+format+"\n", "1;31"), args...)
- os.Exit(1)
-}
+// --- Utilities ---------------------------------------------------------------
-func logFatal(object interface{}) {
- logFatalf("%s", object)
+// Split a string by a set of UTF-8 delimiters, optionally ignoring empty items.
+func splitString(s, delims string, ignoreEmpty bool) (result []string) {
+ for {
+ end := strings.IndexAny(s, delims)
+ if end < 0 {
+ break
+ }
+ if !ignoreEmpty || end != 0 {
+ result = append(result, s[:end])
+ }
+ s = s[end+1:]
+ }
+ if !ignoreEmpty || s != "" {
+ result = append(result, s)
+ }
+ return
}
-func getHome() (home string) {
- if u, _ := user.Current(); u != nil {
- home = u.HomeDir
- } else {
- home = os.Getenv("HOME")
+func findTildeHome(username string) string {
+ if username != "" {
+ if u, _ := user.Lookup(username); u != nil {
+ return u.HomeDir
+ }
+ } else if u, _ := user.Current(); u != nil {
+ return u.HomeDir
+ } else if v, ok := os.LookupEnv("HOME"); ok {
+ return v
}
- return
+ return "~" + username
}
-// Only handling the simple case as that's what one mostly wants.
-// TODO(p): Handle the generic case as well.
+// Tries to expand the tilde in paths, leaving it as-is on error.
func expandTilde(path string) string {
- if strings.HasPrefix(path, "~/") {
- return getHome() + path[1:]
+ if path[0] != '~' {
+ return path
+ }
+
+ var n int
+ for n = 0; n < len(path); n++ {
+ if path[n] == '/' {
+ break
+ }
}
- return path
+ return findTildeHome(path[1:n]) + path[n:]
}
-func getXdgHomeDir(name, def string) string {
+func getXDGHomeDir(name, def string) string {
env := os.Getenv(name)
if env != "" && env[0] == '/' {
return env
}
- return filepath.Join(getHome(), def)
+
+ home := ""
+ if v, ok := os.LookupEnv("HOME"); ok {
+ home = v
+ } else if u, _ := user.Current(); u != nil {
+ home = u.HomeDir
+ }
+ return filepath.Join(home, def)
}
-// Retrieve all XDG base directories for configuration files
-func getXdgConfigDirs() (result []string) {
- home := getXdgHomeDir("XDG_CONFIG_HOME", ".config")
+// Retrieve all XDG base directories for configuration files.
+func getXDGConfigDirs() (result []string) {
+ home := getXDGHomeDir("XDG_CONFIG_HOME", ".config")
if home != "" {
result = append(result, home)
}
@@ -80,56 +126,6 @@ func getXdgConfigDirs() (result []string) {
return
}
-// Read a configuration file with the given basename w/o extension
-func readConfigFile(name string, output interface{}) error {
- var suffix = filepath.Join(projectName, name+".json")
- for _, path := range getXdgConfigDirs() {
- full := filepath.Join(path, suffix)
- file, err := os.Open(full)
- if err != nil {
- if !os.IsNotExist(err) {
- return err
- }
- continue
- }
- defer file.Close()
-
- decoder := json.NewDecoder(file)
- err = decoder.Decode(output)
- if err != nil {
- return fmt.Errorf("%s: %s", full, err)
- }
- return nil
- }
- return errors.New("configuration file not found")
-}
-
-*/
-
-import (
- "bufio"
- "crypto/sha256"
- "crypto/tls"
- "encoding/hex"
- "flag"
- "fmt"
- "io"
- "log"
- "net"
- "os"
- "os/signal"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
- "syscall"
- "time"
-)
-
-var debugMode = false
-
-// --- Utilities ---------------------------------------------------------------
-
//
// Trivial SSL/TLS autodetection. The first block of data returned by Recvfrom
// must be at least three octets long for this to work reliably, but that should
@@ -173,6 +169,35 @@ var config = []struct {
{"bind", []rune(":6667"), "Address of the IRC server"},
}
+/*
+
+// Read a configuration file with the given basename w/o extension.
+func readConfigFile(name string, output interface{}) error {
+ var suffix = filepath.Join(projectName, name+".json")
+ for _, path := range getXDGConfigDirs() {
+ full := filepath.Join(path, suffix)
+ file, err := os.Open(full)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ return err
+ }
+ continue
+ }
+ defer file.Close()
+
+ // TODO: We don't want to use JSON.
+ decoder := json.NewDecoder(file)
+ err = decoder.Decode(output)
+ if err != nil {
+ return fmt.Errorf("%s: %s", full, err)
+ }
+ return nil
+ }
+ return errors.New("configuration file not found")
+}
+
+*/
+
// --- Rate limiter ------------------------------------------------------------
type floodDetector struct {
@@ -231,21 +256,40 @@ func ircToLower(c byte) byte {
return c
}
-// TODO: To support ALL CAPS initialization of maps, perhaps we should use
-// ircToUpper instead.
-// FIXME: This doesn't follow the meaning of strxfrm and perhaps should be
-// renamed to ircNormalize.
-func ircStrxfrm(ident string) string {
+func ircToUpper(c byte) byte {
+ switch c {
+ case '{':
+ return '['
+ case '}':
+ return ']'
+ case '|':
+ return '\\'
+ case '^':
+ return '~'
+ }
+ if c >= 'a' && c <= 'z' {
+ return c - ('a' - 'A')
+ }
+ return c
+}
+
+// Convert identifier to a canonical form for case-insensitive comparisons.
+// ircToUpper is used so that statically initialized maps can be in uppercase.
+func ircToCanon(ident string) string {
var canon []byte
for _, c := range []byte(ident) {
- canon = append(canon, ircToLower(c))
+ canon = append(canon, ircToUpper(c))
}
return string(canon)
}
+func ircEqual(s1, s2 string) bool {
+ return ircToCanon(s1) == ircToCanon(s2)
+}
+
func ircFnmatch(pattern string, s string) bool {
- pattern, s = ircStrxfrm(pattern), ircStrxfrm(s)
- // FIXME: This should not support [] ranges and handle / specially.
+ pattern, s = ircToCanon(pattern), ircToCanon(s)
+ // FIXME: This should not support [] ranges and handle '/' specially.
// We could translate the pattern to a regular expression.
matched, _ := filepath.Match(pattern, s)
return matched
@@ -331,8 +375,8 @@ type client struct {
transport net.Conn // underlying connection
tls *tls.Conn // TLS, if detected
conn connCloseWrite // high-level connection
- inQ []byte // unprocessed input
- outQ []byte // unprocessed output
+ recvQ []byte // unprocessed input
+ sendQ []byte // unprocessed output
reading bool // whether a reading goroutine is running
writing bool // whether a writing goroutine is running
closing bool // whether we're closing the connection
@@ -459,10 +503,8 @@ type writeEvent struct {
var (
started int64 // when has the server been started
- users map[string]*client // maps nicknames to clients
- channels map[string]*channel // maps channel names to data
- handlers map[string]bool // TODO message handlers
- capHandlers map[string]bool // TODO CAP message handlers
+ users map[string]*client // maps nicknames to clients
+ channels map[string]*channel // maps channel names to data
whowas map[string]*whowasInfo // WHOWAS registry
@@ -481,38 +523,38 @@ var (
writes = make(chan writeEvent)
timeouts = make(chan *client)
- tlsConf *tls.Config
- clients = make(map[*client]bool)
- listener net.Listener
- // TODO: quitting, quitTimer as they are named in kike?
- inShutdown bool
- shutdownTimer <-chan time.Time
+ tlsConf *tls.Config
+ clients = make(map[*client]bool)
+ listener net.Listener
+ quitting bool
+ quitTimer <-chan time.Time
)
// Forcefully tear down all connections.
-func forceShutdown(reason string) {
- if !inShutdown {
- log.Fatalln("forceShutdown called without initiateShutdown")
+func forceQuit(reason string) {
+ if !quitting {
+ log.Fatalln("forceQuit called without initiateQuit")
}
log.Printf("forced shutdown (%s)\n", reason)
for c := range clients {
- c.destroy("TODO")
+ // initiateQuit has already unregistered the client.
+ c.kill("Shutting down")
}
}
// Initiate a clean shutdown of the whole daemon.
-func initiateShutdown() {
+func initiateQuit() {
log.Println("shutting down")
if err := listener.Close(); err != nil {
log.Println(err)
}
for c := range clients {
- c.closeLink("TODO")
+ c.closeLink("Shutting down")
}
- shutdownTimer = time.After(5 * time.Second)
- inShutdown = true
+ quitTimer = time.After(5 * time.Second)
+ quitting = true
}
// TODO: ircChannelCreate
@@ -538,32 +580,31 @@ func ircSendToRoommates(c *client, message string) {
// --- Clients (continued) -----------------------------------------------------
-// TODO: Perhaps we should append to *[]byte for performance.
-func clientModeToString(m uint, mode *string) {
+func clientModeToString(m uint, mode *[]byte) {
if 0 != m&ircUserModeInvisible {
- *mode += "i"
+ *mode = append(*mode, 'i')
}
if 0 != m&ircUserModeRxWallops {
- *mode += "w"
+ *mode = append(*mode, 'w')
}
if 0 != m&ircUserModeRestricted {
- *mode += "r"
+ *mode = append(*mode, 'r')
}
if 0 != m&ircUserModeOperator {
- *mode += "o"
+ *mode = append(*mode, 'o')
}
if 0 != m&ircUserModeRxServerNotices {
- *mode += "s"
+ *mode = append(*mode, 's')
}
}
func (c *client) getMode() string {
- mode := ""
+ var mode []byte
if c.awayMessage != "" {
- mode += "a"
+ mode = append(mode, 'a')
}
clientModeToString(c.mode, &mode)
- return mode
+ return string(mode)
}
func (c *client) send(line string) {
@@ -571,14 +612,13 @@ func (c *client) send(line string) {
return
}
- // TODO: Rename inQ and outQ to recvQ and sendQ as they are usually named.
- oldOutQ := len(c.outQ)
+ oldSendQLen := len(c.sendQ)
// So far there's only one message tag we use, so we can do it simple;
// note that a 1024-character limit applies to messages with tags on.
if 0 != c.capsEnabled&ircCapServerTime {
- c.outQ = time.Now().UTC().
- AppendFormat(c.outQ, "@time=2006-01-02T15:04:05.000Z ")
+ c.sendQ = time.Now().UTC().
+ AppendFormat(c.sendQ, "@time=2006-01-02T15:04:05.000Z ")
}
bytes := []byte(line)
@@ -587,13 +627,13 @@ func (c *client) send(line string) {
}
// TODO: Kill the connection above some "SendQ" threshold (careful!)
- c.outQ = append(c.outQ, bytes...)
- c.outQ = append(c.outQ, "\r\n"...)
- c.flushOutQ()
+ c.sendQ = append(c.sendQ, bytes...)
+ c.sendQ = append(c.sendQ, "\r\n"...)
+ c.flushSendQ()
// Technically we haven't sent it yet but that's a minor detail
c.nSentMessages++
- c.sentBytes += len(c.outQ) - oldOutQ
+ c.sentBytes += len(c.sendQ) - oldSendQLen
}
func (c *client) sendf(format string, a ...interface{}) {
@@ -604,7 +644,14 @@ func (c *client) addToWhowas() {
// Only keeping one entry for each nickname.
// TODO: Make sure this list doesn't get too long, for example by
// putting them in a linked list ordered by time.
- whowas[ircStrxfrm(c.nickname)] = newWhowasInfo(c)
+ whowas[ircToCanon(c.nickname)] = newWhowasInfo(c)
+}
+
+func (c *client) nicknameOrStar() string {
+ if c.nickname == "" {
+ return "*"
+ }
+ return c.nickname
}
func (c *client) unregister(reason string) {
@@ -622,14 +669,13 @@ func (c *client) unregister(reason string) {
}
c.addToWhowas()
- delete(users, ircStrxfrm(c.nickname))
+ delete(users, ircToCanon(c.nickname))
c.nickname = ""
c.registered = false
}
-// TODO: Rename to kill.
// Close the connection and forget about the client.
-func (c *client) destroy(reason string) {
+func (c *client) kill(reason string) {
if reason == "" {
reason = "Client exited"
}
@@ -661,25 +707,20 @@ func (c *client) closeLink(reason string) {
// We also want to avoid accidentally writing to the socket before
// address resolution has finished.
if c.conn == nil {
- c.destroy(reason)
+ c.kill(reason)
return
}
if c.closing {
return
}
- nickname := c.nickname
- if nickname == "" {
- nickname = "*"
- }
-
// We push an "ERROR" message to the write buffer and let the writer send
// it, with some arbitrary timeout. The "closing" state makes sure
// that a/ we ignore any successive messages, and b/ that the connection
// is killed after the write buffer is transferred and emptied.
// (Since we send this message, we don't need to call CloseWrite here.)
c.sendf("ERROR :Closing link: %s[%s] (%s)",
- nickname, c.hostname /* TODO host IP? */, reason)
+ c.nicknameOrStar(), c.hostname /* TODO host IP? */, reason)
c.closing = true
c.unregister(reason)
@@ -720,12 +761,7 @@ func (c *client) getTLSCertFingerprint() string {
// XXX: ap doesn't really need to be a slice.
func (c *client) makeReply(id int, ap []interface{}) string {
- nickname := c.nickname
- if nickname == "" {
- nickname = "*"
- }
-
- s := fmt.Sprintf(":%s %03d %s ", serverName, id, nickname)
+ s := fmt.Sprintf(":%s %03d %s ", serverName, id, c.nicknameOrStar())
a := fmt.Sprintf(defaultReplies[id], ap...)
return s + a
}
@@ -809,7 +845,7 @@ func isThisMe(target string) bool {
if ircFnmatch(target, serverName) {
return true
}
- _, ok := users[ircStrxfrm(target)]
+ _, ok := users[ircToCanon(target)]
return ok
}
@@ -821,21 +857,20 @@ func (c *client) sendISUPPORT() {
}
func (c *client) tryFinishRegistration() {
- // TODO: Check if the realname is really required.
- if c.nickname == "" || c.username == "" || c.realname == "" {
+ if c.registered || c.capNegotiating {
return
}
- if c.registered || c.capNegotiating {
+ if c.nickname == "" || c.username == "" {
return
}
c.registered = true
c.sendReply(RPL_WELCOME, c.nickname, c.username, c.hostname)
- c.sendReply(RPL_YOURHOST, serverName, "TODO version")
+ c.sendReply(RPL_YOURHOST, serverName, projectVersion)
// The purpose of this message eludes me.
c.sendReply(RPL_CREATED, time.Unix(started, 0).Format("Mon, 02 Jan 2006"))
- c.sendReply(RPL_MYINFO, serverName, "TODO version",
+ c.sendReply(RPL_MYINFO, serverName, projectVersion,
ircSupportedUserModes, ircSupportedChanModes)
c.sendISUPPORT()
@@ -852,7 +887,7 @@ func (c *client) tryFinishRegistration() {
serverName, c.nickname, c.tlsCertFingerprint)
}
- delete(whowas, ircStrxfrm(c.nickname))
+ delete(whowas, ircToCanon(c.nickname))
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -957,8 +992,6 @@ func (c *client) handleCAPEND(a *ircCapArgs) {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// TODO: Beware of case sensitivity, probably need to index it by ircStrxfrm,
-// which should arguably be named ircToLower and ircToUpper or something.
var ircCapHandlers = map[string]func(*client, *ircCapArgs){
"LS": (*client).handleCAPLS,
"LIST": (*client).handleCAPLIST,
@@ -976,14 +1009,8 @@ func ircHandleCAP(msg *message, c *client) {
return
}
- // TODO: This really does seem to warrant a method.
- nickname := c.nickname
- if nickname == "" {
- nickname = "*"
- }
-
args := &ircCapArgs{
- target: nickname,
+ target: c.nicknameOrStar(),
subcommand: msg.params[0],
fullParams: "",
params: []string{},
@@ -991,12 +1018,10 @@ func ircHandleCAP(msg *message, c *client) {
if len(msg.params) > 1 {
args.fullParams = msg.params[1]
- // TODO: ignore_empty, likely create SplitSkipEmpty
- args.params = strings.Split(args.fullParams, " ")
+ args.params = splitString(args.fullParams, " ", true)
}
- // FIXME: We should ASCII ToUpper the subcommand.
- if fn, ok := ircCapHandlers[ircStrxfrm(args.subcommand)]; !ok {
+ if fn, ok := ircCapHandlers[ircToCanon(args.subcommand)]; !ok {
c.sendReply(ERR_INVALIDCAPCMD, args.subcommand,
"Invalid CAP subcommand")
} else {
@@ -1026,8 +1051,8 @@ func ircHandleNICK(msg *message, c *client) {
return
}
- nicknameNormalized := ircStrxfrm(nickname)
- if client, ok := users[nicknameNormalized]; ok && client != c {
+ nicknameCanon := ircToCanon(nickname)
+ if client, ok := users[nicknameCanon]; ok && client != c {
c.sendReply(ERR_NICKNAMEINUSE, nickname)
return
}
@@ -1043,11 +1068,11 @@ func ircHandleNICK(msg *message, c *client) {
// Release the old nickname and allocate a new one.
if c.nickname != "" {
- delete(users, ircStrxfrm(c.nickname))
+ delete(users, ircToCanon(c.nickname))
}
c.nickname = nickname
- users[nicknameNormalized] = c
+ users[nicknameCanon] = c
c.tryFinishRegistration()
}
@@ -1064,7 +1089,7 @@ func ircHandleUSER(msg *message, c *client) {
username, mode, realname := msg.params[0], msg.params[1], msg.params[3]
- // Unfortunately the protocol doesn't give us any means of rejecting it
+ // Unfortunately, the protocol doesn't give us any means of rejecting it.
if !ircIsValidUsername(username) {
username = "*"
}
@@ -1091,7 +1116,30 @@ func ircHandleUSERHOST(msg *message, c *client) {
return
}
- // TODO
+ var reply []byte
+ for i := 0; i < 5 && i < len(msg.params); i++ {
+ nick := msg.params[i]
+ target := users[ircToCanon(nick)]
+ if target == nil {
+ continue
+ }
+ if i != 0 {
+ reply = append(reply, ' ')
+ }
+
+ reply = append(reply, nick...)
+ if 0 != target.mode&ircUserModeOperator {
+ reply = append(reply, '*')
+ }
+
+ if target.awayMessage != "" {
+ reply = append(reply, "=-"...)
+ } else {
+ reply = append(reply, "=+"...)
+ }
+ reply = append(reply, (target.username + "@" + target.hostname)...)
+ }
+ c.sendReply(RPL_USERHOST, string(reply))
}
func ircHandleLUSERS(msg *message, c *client) {
@@ -1162,8 +1210,8 @@ func ircHandleVERSION(msg *message, c *client) {
postVersion = 1
}
- c.sendReply(RPL_VERSION, "TODO version", postVersion, serverName,
- "TODO program name"+" "+"TODO version")
+ c.sendReply(RPL_VERSION, projectVersion, postVersion, serverName,
+ projectName+" "+projectVersion)
c.sendISUPPORT()
}
@@ -1199,13 +1247,13 @@ func ircHandleMODE(msg *message, c *client) {
// TODO
target := msg.params[0]
- client := users[ircStrxfrm(target)]
- ch := users[ircStrxfrm(target)]
+ client := users[ircToCanon(target)]
+ ch := users[ircToCanon(target)]
if client != nil {
- // TODO: Think about strcmp.
- //if ircStrcmp(target, c.nickname) != 0 {
- //}
+ // TODO
+ if ircEqual(target, c.nickname) {
+ }
} else if ch != nil {
// TODO
}
@@ -1223,11 +1271,11 @@ func ircHandleUserMessage(msg *message, c *client,
}
target, text := msg.params[0], msg.params[1]
- if client, ok := users[ircStrxfrm(target)]; ok {
+ if client, ok := users[ircToCanon(target)]; ok {
// TODO
_ = client
_ = text
- } else if ch, ok := channels[ircStrxfrm(target)]; ok {
+ } else if ch, ok := channels[ircToCanon(target)]; ok {
// TODO
_ = ch
} else {
@@ -1254,7 +1302,7 @@ func (c *client) onPrepared(host string, isTLS bool) {
c.hostname = host
c.address = net.JoinHostPort(host, c.port)
- // TODO: If we've tried to send any data before now, we need to flushOutQ.
+ // TODO: If we've tried to send any data before now, we need to flushSendQ.
go read(c)
c.reading = true
}
@@ -1266,16 +1314,16 @@ func (c *client) onRead(data []byte, readErr error) {
return
}
- c.inQ = append(c.inQ, data...)
+ c.recvQ = append(c.recvQ, data...)
for {
// XXX: This accepts even simple LF newlines, even though they're not
// really allowed by the protocol.
- advance, token, _ := bufio.ScanLines(c.inQ, false /* atEOF */)
+ advance, token, _ := bufio.ScanLines(c.recvQ, false /* atEOF */)
if advance == 0 {
break
}
- c.inQ = c.inQ[advance:]
+ c.recvQ = c.recvQ[advance:]
line := string(token)
log.Printf("-> %s\n", line)
@@ -1298,18 +1346,18 @@ func (c *client) onRead(data []byte, readErr error) {
if readErr != io.EOF {
log.Println(readErr)
- c.destroy("TODO")
+ c.kill(readErr.Error())
} else if c.closing {
// Disregarding whether a clean shutdown has happened or not.
log.Println("client finished shutdown")
- c.destroy("TODO")
+ c.kill("TODO")
} else {
log.Println("client EOF")
c.closeLink("")
}
- } else if len(c.inQ) > 8192 {
- log.Println("client inQ overrun")
- c.closeLink("inQ overrun")
+ } else if len(c.recvQ) > 8192 {
+ log.Println("client recvQ overrun")
+ c.closeLink("recvQ overrun")
// tls.Conn doesn't have the CloseRead method (and it needs to be able
// to read from the TCP connection even for writes, so there isn't much
@@ -1319,29 +1367,29 @@ func (c *client) onRead(data []byte, readErr error) {
}
}
-// Spawn a goroutine to flush the outQ if possible and necessary.
-func (c *client) flushOutQ() {
+// Spawn a goroutine to flush the sendQ if possible and necessary.
+func (c *client) flushSendQ() {
if !c.writing && c.conn != nil {
- go write(c, c.outQ)
+ go write(c, c.sendQ)
c.writing = true
}
}
// Handle the results from trying to write to the client connection.
func (c *client) onWrite(written int, writeErr error) {
- c.outQ = c.outQ[written:]
+ c.sendQ = c.sendQ[written:]
c.writing = false
if writeErr != nil {
log.Println(writeErr)
- c.destroy("TODO")
- } else if len(c.outQ) > 0 {
- c.flushOutQ()
+ c.kill(writeErr.Error())
+ } else if len(c.sendQ) > 0 {
+ c.flushSendQ()
} else if c.closing {
if c.reading {
c.conn.CloseWrite()
} else {
- c.destroy("TODO")
+ c.kill("TODO")
}
}
}
@@ -1419,7 +1467,7 @@ func read(client *client) {
}
}
-// Flush outQ, which is passed by parameter so that there are no data races.
+// Flush sendQ, which is passed by parameter so that there are no data races.
func write(client *client, data []byte) {
// We just write as much as we can, the main goroutine does the looping.
n, err := client.conn.Write(data)
@@ -1431,14 +1479,14 @@ func write(client *client, data []byte) {
func processOneEvent() {
select {
case <-sigs:
- if inShutdown {
- forceShutdown("requested by user")
+ if quitting {
+ forceQuit("requested by user")
} else {
- initiateShutdown()
+ initiateQuit()
}
- case <-shutdownTimer:
- forceShutdown("timeout")
+ case <-quitTimer:
+ forceQuit("timeout")
case conn := <-conns:
log.Println("accepted client connection")
@@ -1480,7 +1528,7 @@ func processOneEvent() {
case c := <-timeouts:
if _, ok := clients[c]; ok {
log.Println("client timeouted")
- c.destroy("TODO")
+ c.kill("TODO")
}
}
}
@@ -1490,14 +1538,12 @@ func main() {
version := flag.Bool("version", false, "show version and exit")
flag.Parse()
- // TODO: Consider using the same version number for all subprojects.
if *version {
- fmt.Printf("%s %s\n", "hid", "0")
+ fmt.Printf("%s %s\n", projectName, projectVersion)
return
}
- // TODO: Configuration--create an INI parser, probably;
- // lift XDG_CONFIG_HOME from gitlab-notifier.
+ // TODO: Configuration--create an INI parser, probably.
if len(flag.Args()) != 3 {
log.Fatalf("usage: %s KEY CERT ADDRESS\n", os.Args[0])
}
@@ -1520,7 +1566,7 @@ func main() {
go accept(listener)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
- for !inShutdown || len(clients) > 0 {
+ for !quitting || len(clients) > 0 {
processOneEvent()
}
}