func lineWorker(die dieCh, f *os.File, cfg logplexc.Config, sr *serveRecord) { cfg.Logplex = sr.u target, err := logplexc.NewClient(&cfg) if err != nil { log.Fatalf("could not create logging client: %v", err) } watcher, err := fsnotify.NewWatcher() if err != nil { log.Printf("can't create watcher: %v", err) } defer watcher.Close() r := bufio.NewReader(f) go func() { for { select { case <-die: return case event := <-watcher.Events: if event.Op&fsnotify.Write == fsnotify.Write { for { l, err := r.ReadBytes('\n') // Allow service specific changes l = parseLog(sr, l) // Don't emit empty lines l = bytes.TrimSpace(l) if len(l) == 0 { break } // Append log prefix l = append([]byte(fmt.Sprintf("%s ", sr.Prefix)), l...) // Send the log line target.BufferMessage(134, time.Now(), sr.Service, sr.Service, l) if err != nil { if err == io.EOF { break } log.Printf("unexpected read error: %v", err) } } } case err := <-watcher.Errors: log.Printf("unexpected fs watch error %v:", err) } } }() if err := watcher.Add(f.Name()); err != nil { log.Printf("can't add watcher: %v", err) } <-die }
func syslogWorker(die dieCh, conn net.PacketConn, cfg logplexc.Config, sr *serveRecord) { // Make world-writable so anything can connect and send logs. // This may be be worth locking down more, but as-is unless // pg_logplexcollector and the Postgres server share the same // running user common umasks will be useless. fi, err := os.Stat(sr.P) if err != nil { log.Fatalf( "exiting, cannot stat just created socket %q: %v", sr.P, err) } err = os.Chmod(sr.P, fi.Mode().Perm()|0222) if err != nil { log.Fatalf( "exiting, cannot make just created socket "+ "world-writable %q: %v", sr.P, err) } cfg.Logplex = sr.u buf := make([]byte, 9*KB) target, err := logplexc.NewClient(&cfg) if err != nil { log.Fatalf("could not create auditing client: %v", err) } for { select { case <-die: return default: break } err := conn.SetReadDeadline(time.Now().Add(time.Duration(1 * time.Second))) if err != nil { log.Fatalf("could not set connection deadline: %v", err) } n, _, err := conn.ReadFrom(buf) if n > 0 { // Just send the message wholesale, which // leads to some weird syslog-in-syslog // framing, but perhaps it's good enough. target.BufferMessage(134, time.Now(), "audit", "-", append([]byte( "instance_type=shogun identity="+ sr.I+" "), buf[:n]...)) } if err != nil { if err, ok := err.(net.Error); ok { if err.Timeout() || err.Temporary() { continue } } log.Fatalf("got syslog datagram error %v", err) } } }
func logWorker(die dieCh, l net.Listener, cfg logplexc.Config, sr *serveRecord) { // Make world-writable so anything can connect and send logs. // This may be be worth locking down more, but as-is unless // pg_logplexcollector and the Postgres server share the same // running user common umasks will be useless. fi, err := os.Stat(sr.P) if err != nil { log.Fatalf( "exiting, cannot stat just created socket %q: %v", sr.P, err) } err = os.Chmod(sr.P, fi.Mode().Perm()|0222) if err != nil { log.Fatalf( "exiting, cannot make just created socket "+ "world-writable %q: %v", sr.P, err) } for { select { case <-die: log.Print("listener exits normally from die request") return default: break } conn, err := l.Accept() if err != nil { log.Printf("accept error: %v", err) } if err != nil { log.Fatalf("serve database suffers unrecoverable "+ "error: %v", err) } go func() { stream := core.NewBackendStream(conn) var exit exitFn exit = func(args ...interface{}) { if len(args) == 1 { log.Printf("Disconnect client: %v", args[0]) } else if len(args) > 1 { if s, ok := args[0].(string); ok { log.Printf(s, args[1:]...) } else { // Not an intended use case, but do // one's best to print something. log.Printf("Got a malformed exit: %v", args) } } panic(&exit) } // Recovers from panic and exits in an orderly manner if (and // only if) exit() is called; otherwise propagate the panic // normally. defer func() { conn.Close() // &exit is used as a sentinel value. if r := recover(); r != nil && r != &exit { panic(r) } }() var msgInit msgInit msgInit = func(m *core.Message, exit exitFn) { err = stream.Next(m) if err == io.EOF { exit("postgres client disconnects") } else if err != nil { exit("could not read next message: %v", err) } } // Protocol start-up; packets that are only received once. processVerMsg(msgInit, exit) ident := processIdentMsg(msgInit, exit) log.Printf("client connects with identifier %q", ident) // Resolve the identifier to a serve if sr.I != ident { exit("got unexpected identifier for socket: "+ "path %s, expected %s, got %s", sr.P, sr.I, ident) } // Set up client with serve client := func(cfg logplexc.Config, url *url.URL) *logplexc.Client { cfg.Logplex = *url client, err := logplexc.NewClient(&cfg) if err != nil { exit(err) } return client } primary := client(cfg, &sr.u) var audit *logplexc.Client if sr.audit != nil { audit = client(cfg, sr.audit) } defer func() { primary.Close() if audit != nil { audit.Close() } }() processLogMsg(die, primary, audit, msgInit, sr, exit) }() } }