Compare commits

..

No commits in common. "master" and "set-syslog-fmt" have entirely different histories.

12 changed files with 55 additions and 207 deletions

2
.gitignore vendored
View file

@ -1,2 +0,0 @@
.*.vim
/build

3
.gitmodules vendored
View file

@ -1,3 +0,0 @@
[submodule "vendor/gopkg.in/mcuadros/go-syslog.v2"]
path = vendor/gopkg.in/mcuadros/go-syslog.v2
url = https://github.com/boyska/go-syslog

View file

@ -54,8 +54,8 @@ func (b *BoolAuto) Set(s string) error {
} }
func main() { func main() {
queryAddr := flag.String("addr", "", "http service address") addr := flag.String("addr", "localhost:9080", "http service address")
querySocket := flag.String("socket", "/tmp/circologd-query.sock", "Path to a unix domain socket for the HTTP server") querySocket := flag.String("socket", "", "Path to a unix domain socket for the HTTP server")
backlogLimit := flag.Int("n", -1, "Limit the backlog length, defaults to no limit (-1)") backlogLimit := flag.Int("n", -1, "Limit the backlog length, defaults to no limit (-1)")
var format formatter.Format var format formatter.Format
format = formatter.FormatSyslog format = formatter.FormatSyslog
@ -75,7 +75,7 @@ func main() {
signal.Notify(interrupt, os.Interrupt) signal.Notify(interrupt, os.Interrupt)
var d *websocket.Dialer var d *websocket.Dialer
u := url.URL{Scheme: "ws", u := url.URL{Scheme: "ws",
Host: *queryAddr, // ignored in case of -socket; see the Dialer below Host: *addr, // ignored in case of -socket; see the Dialer below
Path: "/ws", Path: "/ws",
} }
q := u.Query() q := u.Query()
@ -84,7 +84,7 @@ func main() {
q.Set("l", strconv.Itoa(*backlogLimit)) q.Set("l", strconv.Itoa(*backlogLimit))
} }
u.RawQuery = q.Encode() u.RawQuery = q.Encode()
if *queryAddr == "" { if *querySocket != "" {
d = &websocket.Dialer{ d = &websocket.Dialer{
NetDial: func(network, addr string) (net.Conn, error) { NetDial: func(network, addr string) (net.Conn, error) {
return net.Dial("unix", *querySocket) return net.Dial("unix", *querySocket)
@ -95,7 +95,7 @@ func main() {
log.Printf("connecting to %s", *querySocket) log.Printf("connecting to %s", *querySocket)
} else { } else {
d = websocket.DefaultDialer d = websocket.DefaultDialer
log.Printf("connecting to %s", *queryAddr) log.Printf("connecting to %s", *addr)
} }
c, _, err := d.Dial(u.String(), nil) c, _, err := d.Dial(u.String(), nil)
@ -149,7 +149,7 @@ func main() {
select { select {
case <-done: case <-done:
log.Println("Successfully close") log.Println("Successfully close")
case <-time.After(5 * time.Second): case <-time.After(1 * time.Second):
log.Println("Forced close") log.Println("Forced close")
} }
return return

View file

@ -1,39 +0,0 @@
package main
import (
"net"
"github.com/coreos/go-systemd/activation"
)
func Listeners() ([]net.Listener, error) {
files := activation.Files(false)
listeners := make([]net.Listener, len(files))
for i, f := range files {
if pc, err := net.FileListener(f); err == nil {
listeners[i] = pc
f.Close()
}
}
return listeners, nil
}
// PacketConns returns a slice containing a net.PacketConn for each matching socket type
// passed to this process.
//
// The order of the file descriptors is preserved in the returned slice.
// Nil values are used to fill any gaps. For example if systemd were to return file descriptors
// corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn}
func PacketConns() ([]net.PacketConn, error) {
files := activation.Files(false)
conns := make([]net.PacketConn, len(files))
for i, f := range files {
if pc, err := net.FilePacketConn(f); err == nil {
conns[i] = pc
f.Close()
}
}
return conns, nil
}

View file

@ -153,15 +153,6 @@ func getWSHandler(hub circolog.Hub) http.HandlerFunc {
hub.Unregister <- c hub.Unregister <- c
conn.Close() conn.Close()
}() }()
go func() {
for {
_, _, err := conn.ReadMessage()
if err != nil {
conn.Close()
return
}
}
}()
for { for {
select { select {
case message, ok := <-c.Messages: case message, ok := <-c.Messages:

View file

@ -17,29 +17,22 @@ import (
syslog "gopkg.in/mcuadros/go-syslog.v2" syslog "gopkg.in/mcuadros/go-syslog.v2"
) )
var socketsToRemove []string
func cleanSocket(socket string) { func cleanSocket(socket string) {
if err := os.Remove(socket); err != nil { if err := os.Remove(socket); err != nil {
fmt.Fprintln(os.Stderr, "Error cleaning", socket, ":", err) fmt.Fprintln(os.Stderr, "Error cleaning", socket, ":", err)
} }
} }
func removeAtExit(socket string) {
socketsToRemove = append(socketsToRemove, socket)
}
func main() { func main() {
var err error var err error
var syslogSocket SyslogSocket
var logFmt formatter.SyslogRFC var logFmt formatter.SyslogRFC
logFmt.Format = syslog.Automatic logFmt = formatter.Auto
flag.Var(&syslogSocket, "syslogd-socket", "The socket to listen to syslog addresses") syslogSocketPath := flag.String("syslogd-socket", "", "The socket to listen to syslog addresses")
// dumpSocketPath := flag.String("dump-socket", "/run/buffer.sock", "The socket that user will connect to in order to receive logs") // dumpSocketPath := flag.String("dump-socket", "/run/buffer.sock", "The socket that user will connect to in order to receive logs")
bufsize := flag.Int("buffer-size", 1000, "Number of messages to keep") bufsize := flag.Int("buffer-size", 1000, "Number of messages to keep")
syslogAddr := flag.String("syslog-addr", "127.0.0.1:9514", "Address:port where to listen for syslog messages") syslogAddr := flag.String("syslog-addr", "127.0.0.1:9514", "Address:port where to listen for syslog messages")
queryAddr := flag.String("query-addr", "", "Address:port where to bind the query service") queryAddr := flag.String("query-addr", "127.0.0.1:9080", "Address:port where to bind the query service")
querySocket := flag.String("query-socket", "/tmp/circologd-query.sock", "Path to a unix domain socket for the HTTP server; recommended for security reasons!") querySocket := flag.String("query-socket", "", "Path to a unix domain socket for the HTTP server; recommended for security reasons!")
ctlSocket := flag.String("ctl-socket", "/tmp/circologd-ctl.sock", "Path to a unix domain socket for the control server; leave empty to disable") ctlSocket := flag.String("ctl-socket", "/tmp/circologd-ctl.sock", "Path to a unix domain socket for the control server; leave empty to disable")
flag.Var(&logFmt, "log-fmt", "Log messages format. If not set, defaults to automatic choice. Allowed values: rfc3164, rfc5424, auto.") flag.Var(&logFmt, "log-fmt", "Log messages format. If not set, defaults to automatic choice. Allowed values: rfc3164, rfc5424, auto.")
verbose := flag.Bool("verbose", false, "Print more output executing the daemon") verbose := flag.Bool("verbose", false, "Print more output executing the daemon")
@ -54,27 +47,15 @@ func main() {
go hub.Run() go hub.Run()
server := syslog.NewServer() server := syslog.NewServer()
server.SetFormat(logFmt.Format) formatter.SetSyslogFormat(server, logFmt)
fmt.Printf("Syslog format set to: %s\n", logFmt.String())
server.SetHandler(handler) server.SetHandler(handler)
if syslogSocket.isSocketActivated { if *syslogSocketPath != "" {
fmt.Printf("Binding to socket `%s` [syslog]\n", syslogSocket.String()) if err = server.ListenUnixgram(*syslogSocketPath); err != nil {
if syslogSocket.Listener != nil {
fmt.Println("(stream)")
server.Listen(syslogSocket.Listener)
} else {
fmt.Println("(datagram)", syslogSocket.Conn)
server.ListenDgram(syslogSocket.Conn)
}
} else {
syslogSocketPath := syslogSocket.Path
if syslogSocketPath != "" {
if err = server.ListenUnixgram(syslogSocketPath); err != nil {
fmt.Fprintln(os.Stderr, "argh", err) fmt.Fprintln(os.Stderr, "argh", err)
os.Exit(1) os.Exit(1)
} }
fmt.Printf("Binding socket `%s` [syslog]\n", syslogSocketPath) defer cleanSocket(*syslogSocketPath)
removeAtExit(syslogSocketPath) fmt.Printf("Binding socket `%s` [syslog]\n", *syslogSocketPath)
} else { } else {
fmt.Printf("Binding address `%s` [syslog]\n", *syslogAddr) fmt.Printf("Binding address `%s` [syslog]\n", *syslogAddr)
if err = server.ListenUDP(*syslogAddr); err != nil { if err = server.ListenUDP(*syslogAddr); err != nil {
@ -82,21 +63,20 @@ func main() {
os.Exit(1) os.Exit(1)
} }
} }
}
if err = server.Boot(); err != nil { if err = server.Boot(); err != nil {
fmt.Fprintln(os.Stderr, "argh", err) fmt.Fprintln(os.Stderr, "argh", err)
os.Exit(1) os.Exit(1)
} }
httpQueryServer := http.Server{Handler: setupHTTP(hub)} httpQueryServer := http.Server{Handler: setupHTTP(hub)}
if *queryAddr == "" { if *querySocket != "" {
fmt.Printf("Binding address `%s` [http]\n", *querySocket) fmt.Printf("Binding address `%s` [http]\n", *querySocket)
unixListener, err := net.Listen("unix", *querySocket) unixListener, err := net.Listen("unix", *querySocket)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, "Error binding HTTP unix domain socket", err) fmt.Fprintln(os.Stderr, "Error binding HTTP unix domain socket", err)
return return
} }
removeAtExit(*querySocket) defer cleanSocket(*querySocket)
go func() { go func() {
if err := httpQueryServer.Serve(unixListener); err != nil && err != http.ErrServerClosed { if err := httpQueryServer.Serve(unixListener); err != nil && err != http.ErrServerClosed {
fmt.Fprintln(os.Stderr, "error binding", *querySocket, ":", err) fmt.Fprintln(os.Stderr, "error binding", *querySocket, ":", err)
@ -121,7 +101,7 @@ func main() {
fmt.Fprintln(os.Stderr, "Error binding HTTP unix domain socket", err) fmt.Fprintln(os.Stderr, "Error binding HTTP unix domain socket", err)
return return
} }
removeAtExit(*ctlSocket) defer cleanSocket(*ctlSocket)
go func() { go func() {
if err := httpCtlServer.Serve(unixListener); err != nil && err != http.ErrServerClosed { if err := httpCtlServer.Serve(unixListener); err != nil && err != http.ErrServerClosed {
fmt.Fprintln(os.Stderr, "error binding:", err) fmt.Fprintln(os.Stderr, "error binding:", err)
@ -171,9 +151,6 @@ func main() {
if err := httpCtlServer.Shutdown(nil); err != nil { if err := httpCtlServer.Shutdown(nil); err != nil {
fmt.Fprintln(os.Stderr, "Error closing control server:", err) fmt.Fprintln(os.Stderr, "Error closing control server:", err)
} }
for _, socket := range socketsToRemove {
cleanSocket(socket)
}
return return
} }
} }

View file

@ -1,50 +0,0 @@
package main
import (
"net"
)
// SyslogSocket is a struct eventually containing a net.Listener
// ready with messages, and a Path in case the Listener is not present.
type SyslogSocket struct {
Listener net.Listener
Conn net.PacketConn
Path string
isSocketActivated bool
}
// Set from command-line
func (s *SyslogSocket) Set(v string) error {
err := s.getActivationSocket()
if err == nil && (s.Conn != nil || s.Listener != nil) {
s.isSocketActivated = true
}
if !s.isSocketActivated {
s.Path = v
}
return nil
}
func (s *SyslogSocket) String() string {
if s.isSocketActivated {
return "systemd-provided"
}
return s.Path
}
func (s *SyslogSocket) getActivationSocket() error {
conns, err := PacketConns()
if err == nil && len(conns) > 0 && conns[0] != nil {
s.Conn = conns[0]
return nil
}
listeners, err := Listeners()
if err != nil {
return err
}
if len(listeners) == 0 {
return nil
}
s.Listener = listeners[0]
return nil
}

View file

@ -69,26 +69,4 @@ Here is a working unit for this:
### journald with socket activation ### journald with socket activation
To run circologd as non-root, while listening on a root-owned socket (`/run/systemd/journal/syslog`) use To run circologd as non-root, while listening on a root-owned socket (`/run/systemd/journal/syslog`) use
socket activation. Create a unit in `/etc/systemd/system/circolog.service`: socket activation
[Unit]
Description=In-memory logging
[Service]
User=nobody
Group=nogroup
ExecStart=/usr/local/sbin/circologd -syslogd-socket "" -buffer-size 2000 -query-socket /run/circolog/query.sock
[Install]
WantedBy=multi-user.target
Then symlink the `syslog.service` unit to the newly created one:
ln -sf /etc/systemd/system/circolog.service /etc/systemd/system/syslog.service
and restart the service:
systemctl daemon-reload
systemctl restart syslog.service
Now circolog is activated and receives messages from `journald`.

View file

@ -4,6 +4,5 @@ nav:
- Install: install.md - Install: install.md
- Queries: query.md - Queries: query.md
- Hacking: hacking.md - Hacking: hacking.md
- Systemd: systemd.md
repo_url: https://git.lattuga.net/boyska/circolog repo_url: https://git.lattuga.net/boyska/circolog
repo_name: 'Repository' repo_name: 'Repository'

View file

@ -2,48 +2,66 @@ package formatter
import ( import (
"errors" "errors"
"log"
syslog "gopkg.in/mcuadros/go-syslog.v2" syslog "gopkg.in/mcuadros/go-syslog.v2"
"gopkg.in/mcuadros/go-syslog.v2/format"
) )
// SyslogRFC is the formatter that the server should use type SyslogRFC string
type SyslogRFC struct{ format.Format }
// SetSyslogFormat does the job of setting the server parser to the provided RFC log format.
func SetSyslogFormat(server *syslog.Server, format SyslogRFC) {
switch {
case format == Auto:
server.SetFormat(syslog.Automatic)
case format == RFC3164:
server.SetFormat(syslog.RFC3164)
case format == RFC5424:
server.SetFormat(syslog.RFC5424)
}
log.Printf("Syslog format set to: %s\n", format)
}
func (rfc *SyslogRFC) Set(v string) error { func (rfc *SyslogRFC) Set(v string) error {
newval, err := parseRFCValue(v) newval, err := parseRFCValue(v)
if err != nil { if err != nil {
return err return err
} }
rfc.Format = newval *rfc = newval
return nil return nil
} }
func (rfc *SyslogRFC) String() string { func (rfc *SyslogRFC) String() string {
switch { switch {
case rfc.Format == syslog.Automatic: case *rfc == Auto:
return "auto" return "auto"
case rfc.Format == syslog.RFC3164: case *rfc == RFC3164:
return "rfc3164" return "rfc3164"
case rfc.Format == syslog.RFC5424: case *rfc == RFC5424:
return "rfc5424" return "rfc5424"
} }
return "" return ""
} }
func parseRFCValue(v string) (format.Format, error) { func parseRFCValue(v string) (SyslogRFC, error) {
switch { switch {
case v == "rfc3164": case v == "rfc3164":
return syslog.RFC3164, nil return RFC3164, nil
case v == "rfc5424": case v == "rfc5424":
return syslog.RFC5424, nil return RFC5424, nil
case v == "auto": case v == "auto":
return syslog.Automatic, nil return Auto, nil
default: default:
return nil, ErrRFCNotSupported return "", ErrRFCNotSupported
} }
} }
const (
RFC3164 = "rfc3164"
RFC5424 = "rfc5424"
Auto = "auto"
)
// ErrRFCNotSupported is raised if the supplied rfc string is // ErrRFCNotSupported is raised if the supplied rfc string is
// not recognized. // not recognized.
var ErrRFCNotSupported = errors.New("RFC not known") var ErrRFCNotSupported = errors.New("RFC not known")

View file

@ -1,20 +0,0 @@
#!/bin/bash
set -u
for goosarch in $(go tool dist list | grep -vw -e aix -e js/wasm -e plan9 -e solaris -e android -e nacl)
do
mkdir -p "build/$goosarch"
goos=$(cut -d/ -f 1 <<<$goosarch)
goarch=$(cut -d/ -f 2 <<<$goosarch)
for cmd in cmd/*; do
GOOS=${goos} GOARCH=${goarch} go build -o "build/$goos/$goarch/$(basename $cmd)" ./$cmd
done
done
find build/ -type f|cut -d/ -f 1-3|uniq|while read -r dir; do
find $dir/ -type f -executable | xargs sha1sum > $dir/SHA1SUMS.txt
# TODO: touch to last commit date maybe
find build -exec touch -d @1234567890 {} \;
zip -q -X -j -r "circolog-$(git describe --tags --always)-$(cut -d/ -f 2-3 <<<"$dir"|tr / -)" "$dir"
done

@ -1 +0,0 @@
Subproject commit 166aad3f993ce4a67bf486e62d637c834c8a8fe6