Example #1
0
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
}
Example #2
0
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)
		}
	}
}
Example #3
0
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)
		}()
	}
}