diff --git a/spongy/irc.go b/spongy/irc.go index 04688b0..28381a5 100644 --- a/spongy/irc.go +++ b/spongy/irc.go @@ -3,10 +3,10 @@ package main import ( "strconv" "strings" - "fmt" ) type Message struct { + Unparsed string Command string FullSender string Sender string @@ -20,6 +20,7 @@ func NewMessage(v string) (Message, error) { var parts []string var lhs string + m.Unparsed = v parts = strings.SplitN(v, " :", 2) if len(parts) == 2 { lhs = parts[0] @@ -98,11 +99,9 @@ func NewMessage(v string) (Message, error) { } func (m Message) String() string { - args := strings.Join(m.Args, " ") - return fmt.Sprintf("%s %s %s %s %s :%s", m.FullSender, m.Command, m.Sender, m.Forum, args, m.Text) + return m.Unparsed } - func SplitTarget(s string) (string, string, string) { var parts []string @@ -130,4 +129,3 @@ func IsChannel(s string) bool { return false } } - diff --git a/spongy/logfile.go b/spongy/logfile.go index 0044891..6e6da2d 100644 --- a/spongy/logfile.go +++ b/spongy/logfile.go @@ -3,6 +3,7 @@ package main import ( "fmt" "os" + "log" "path" "time" ) @@ -13,19 +14,56 @@ type Logfile struct { name string nlines int maxlines int + outq chan string +} + +func timestamp(s string) string { + ret := fmt.Sprintf("%d %s", time.Now().Unix(), s) + return ret } func NewLogfile(baseDir string, maxlines int) (*Logfile) { - return &Logfile{baseDir, nil, "", 0, maxlines} + lf := Logfile{baseDir, nil, "", 0, maxlines, make(chan string, 50)} + go lf.processQueue(); + return &lf } func (lf *Logfile) Close() { if lf.file != nil { - lf.writeln("EXIT") - lf.file.Close() + lf.Log("EXIT") + close(lf.outq) } } +func (lf *Logfile) Log(s string) error { + lf.outq <- timestamp(s) + return nil +} + +// +// + +func (lf *Logfile) processQueue() { + for line := range lf.outq { + if (lf.file == nil) || (lf.nlines >= lf.maxlines) { + if err := lf.rotate(); err != nil { + // Just keep trying, I guess. + log.Print(err) + continue + } + lf.nlines = 0 + } + + if _, err := fmt.Fprintln(lf.file, line); err != nil { + log.Print(err) + continue + } + lf.nlines += 1 + } + + lf.file.Close() +} + func (lf *Logfile) writeln(s string) error { _, err := fmt.Fprintf(lf.file, "%d %s\n", time.Now().Unix(), s) if err == nil { @@ -52,8 +90,8 @@ func (lf *Logfile) rotate() error { if lf.file != nil { // Note location of new log - logmsg := fmt.Sprintf(". NEXTLOG %s", fn) - lf.writeln(logmsg) + logmsg := fmt.Sprintf("NEXTLOG %s", fn) + lf.writeln(timestamp(logmsg)) // All done with the current log lf.file.Close() @@ -66,27 +104,10 @@ func (lf *Logfile) rotate() error { os.Remove(currentPath) os.Symlink(fn, currentPath) - logmsg := fmt.Sprintf(". PREVLOG %s", lf.name) - lf.writeln(logmsg) + logmsg := fmt.Sprintf("PREVLOG %s", lf.name) + lf.writeln(timestamp(logmsg)) lf.name = fn return nil } - -func (lf *Logfile) Log(s string) error { - if lf.file == nil { - lf.rotate() - } - - err := lf.writeln(s) - if err == nil { - return err - } - - if lf.nlines >= lf.maxlines { - return lf.rotate() - } - - return nil -} diff --git a/spongy/network.go b/spongy/network.go index ae17462..1b445fb 100644 --- a/spongy/network.go +++ b/spongy/network.go @@ -9,6 +9,7 @@ import ( "net" "os" "os/user" + "os/exec" "path" "strings" "time" @@ -48,7 +49,9 @@ type Network struct { serverIndex int conn io.ReadWriteCloser - logq chan Message + + logf *Logfile + inq chan string outq chan string } @@ -57,23 +60,21 @@ func NewNetwork(basePath string) *Network { nw := Network{ running: true, basePath: basePath, - logq: make(chan Message, 20), } - - go nw.LogLoop() + nw.logf = NewLogfile(nw.basePath, int(maxlogsize)) return &nw } func (nw *Network) Close() { nw.running = false - close(nw.logq) if nw.conn != nil { nw.conn.Close() } + nw.logf.Close() } -func (nw *Network) WatchOutqDirectory() { +func (nw *Network) watchOutqDirectory() { outqDirname := path.Join(nw.basePath, "outq") dir, err := os.Open(outqDirname) @@ -113,20 +114,12 @@ func (nw *Network) HandleInfile(fn string) { } } -func (nw *Network) LogLoop() { - logf := NewLogfile(nw.basePath, int(maxlogsize)) - defer logf.Close() - - for m := range nw.logq { - logf.Log(m.String()) - } -} - -func (nw *Network) ServerWriteLoop() { +func (nw *Network) serverWriteLoop() { for v := range nw.outq { - m, _ := NewMessage(v) - nw.logq <- m + debug("ยป %s", v) + nw.logf.Log(v) fmt.Fprintln(nw.conn, v) + time.Sleep(500 * time.Millisecond) } } @@ -163,21 +156,21 @@ func (nw *Network) JoinChannels() { } for _, ch := range chans { + debug("Joining %s", ch) nw.outq <- "JOIN " + ch } } -func (nw *Network) MessageDispatch() { +func (nw *Network) messageDispatchLoop() { for line := range nw.inq { + nw.logf.Log(line) + m, err := NewMessage(line) if err != nil { log.Print(err) continue } - nw.logq <- m - // XXX: Add in a handler subprocess call - switch m.Command { case "PING": nw.outq <- "PONG :" + m.Text @@ -186,6 +179,30 @@ func (nw *Network) MessageDispatch() { case "433": nw.NextNick() } + + handlerPath := path.Join(nw.basePath, "handler") + cmd := exec.Command(handlerPath, m.Args...) + cmd.Env = []string{ + "command=" + m.Command, + "fullsender=" + m.FullSender, + "sender=" + m.Sender, + "forum=" + m.Forum, + "text=" + m.Text, + "raw=" + line, + } + cmd.Stderr = os.Stderr + out, err := cmd.Output() + if err != nil { + log.Print(err) + continue + } + + if len(out) > 0 { + outlines := strings.Split(string(out), "\n") + for _, line := range outlines { + nw.outq <- line + } + } } } @@ -201,6 +218,7 @@ func (nw *Network) ConnectToNextServer() bool { } server := servers[nw.serverIndex] + debug("Connecting to %s", server) switch (server[0]) { case '|': parts := strings.Split(server[1:], " ") @@ -219,6 +237,7 @@ func (nw *Network) ConnectToNextServer() bool { log.Print(err) return false } + debug("Connected") return true } @@ -246,8 +265,15 @@ func (nw *Network) login() { nw.NextNick() } +func (nw *Network) keepaliveLoop() { + for nw.running { + time.Sleep(1 * time.Minute) + nw.outq <- "PING :keepalive" + } +} -func (nw *Network) Connect(){ + +func (nw *Network) Connect() { for nw.running { if ! nw.ConnectToNextServer() { time.Sleep(8 * time.Second) @@ -257,9 +283,10 @@ func (nw *Network) Connect(){ nw.inq = make(chan string, 20) nw.outq = make(chan string, 20) - go nw.ServerWriteLoop() - go nw.MessageDispatch() - go nw.WatchOutqDirectory() + go nw.serverWriteLoop() + go nw.messageDispatchLoop() + go nw.watchOutqDirectory() + go nw.keepaliveLoop() nw.login() diff --git a/spongy/spongy.go b/spongy/spongy.go index 30b18a8..bc6f85c 100644 --- a/spongy/spongy.go +++ b/spongy/spongy.go @@ -11,8 +11,15 @@ import ( ) var running bool = true +var verbose bool = false var maxlogsize uint +func debug(format string, a ...interface{}) { + if verbose { + log.Printf(format, a...) + } +} + func exists(filename string) bool { _, err := os.Stat(filename); if err != nil { return false @@ -78,8 +85,9 @@ func usage() { func main() { flag.Usage = usage - flag.UintVar(&maxlogsize, "logsize", 1000, "Log entries before rotating") - notime := flag.Bool("notime", false, "Don't timestamp log messages") + flag.UintVar(&maxlogsize, "logsize", 6000, "Log entries before rotating") + flag.BoolVar(&verbose, "verbose", false, "Verbose logging") + notime := flag.Bool("notime", false, "Don't timestamp debugging messages") flag.Parse() if flag.NArg() != 1 { usage()