Compare commits

..

6 commits

Author SHA1 Message Date
f7caefdae1
Document socket activation. 2019-05-03 14:26:16 +02:00
b0cd779d61 Supports datagram socket activation 2019-05-03 12:05:27 +02:00
7937d8b4c3
Ignore .vim files 2019-05-03 10:48:28 +02:00
3e27cad5b1
Allow socket activation from systemd. 2019-05-03 10:47:45 +02:00
a2de164a10
Change revision of go-syslog. 2019-05-03 10:45:35 +02:00
9728b236ed
Clean sockets at exit. 2019-05-03 10:32:18 +02:00
7 changed files with 150 additions and 15 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.*.vim

View file

@ -0,0 +1,39 @@
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

@ -17,17 +17,24 @@ import (
syslog "gopkg.in/mcuadros/go-syslog.v2"
)
var socketsToRemove []string
func cleanSocket(socket string) {
if err := os.Remove(socket); err != nil {
fmt.Fprintln(os.Stderr, "Error cleaning", socket, ":", err)
}
}
func removeAtExit(socket string) {
socketsToRemove = append(socketsToRemove, socket)
}
func main() {
var err error
var syslogSocket SyslogSocket
var logFmt formatter.SyslogRFC
logFmt.Format = syslog.Automatic
syslogSocketPath := flag.String("syslogd-socket", "", "The socket to listen to syslog addresses")
flag.Var(&syslogSocket, "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")
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")
@ -50,18 +57,30 @@ func main() {
server.SetFormat(logFmt.Format)
fmt.Printf("Syslog format set to: %s\n", logFmt.String())
server.SetHandler(handler)
if *syslogSocketPath != "" {
if err = server.ListenUnixgram(*syslogSocketPath); err != nil {
fmt.Fprintln(os.Stderr, "argh", err)
os.Exit(1)
if syslogSocket.isSocketActivated {
fmt.Printf("Binding to socket `%s` [syslog]\n", syslogSocket.String())
if syslogSocket.Listener != nil {
fmt.Println("(stream)")
server.Listen(syslogSocket.Listener)
} else {
fmt.Println("(datagram)", syslogSocket.Conn)
server.ListenDgram(syslogSocket.Conn)
}
defer cleanSocket(*syslogSocketPath)
fmt.Printf("Binding socket `%s` [syslog]\n", *syslogSocketPath)
} else {
fmt.Printf("Binding address `%s` [syslog]\n", *syslogAddr)
if err = server.ListenUDP(*syslogAddr); err != nil {
fmt.Fprintln(os.Stderr, "argh", err)
os.Exit(1)
syslogSocketPath := syslogSocket.Path
if syslogSocketPath != "" {
if err = server.ListenUnixgram(syslogSocketPath); err != nil {
fmt.Fprintln(os.Stderr, "argh", err)
os.Exit(1)
}
fmt.Printf("Binding socket `%s` [syslog]\n", syslogSocketPath)
removeAtExit(syslogSocketPath)
} else {
fmt.Printf("Binding address `%s` [syslog]\n", *syslogAddr)
if err = server.ListenUDP(*syslogAddr); err != nil {
fmt.Fprintln(os.Stderr, "argh", err)
os.Exit(1)
}
}
}
if err = server.Boot(); err != nil {
@ -77,7 +96,7 @@ func main() {
fmt.Fprintln(os.Stderr, "Error binding HTTP unix domain socket", err)
return
}
defer cleanSocket(*querySocket)
removeAtExit(*querySocket)
go func() {
if err := httpQueryServer.Serve(unixListener); err != nil && err != http.ErrServerClosed {
fmt.Fprintln(os.Stderr, "error binding", *querySocket, ":", err)
@ -102,7 +121,7 @@ func main() {
fmt.Fprintln(os.Stderr, "Error binding HTTP unix domain socket", err)
return
}
defer cleanSocket(*ctlSocket)
removeAtExit(*ctlSocket)
go func() {
if err := httpCtlServer.Serve(unixListener); err != nil && err != http.ErrServerClosed {
fmt.Fprintln(os.Stderr, "error binding:", err)
@ -152,6 +171,9 @@ func main() {
if err := httpCtlServer.Shutdown(nil); err != nil {
fmt.Fprintln(os.Stderr, "Error closing control server:", err)
}
for _, socket := range socketsToRemove {
cleanSocket(socket)
}
return
}
}

50
cmd/circologd/sockets.go Normal file
View file

@ -0,0 +1,50 @@
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,4 +69,26 @@ Here is a working unit for this:
### journald with socket activation
To run circologd as non-root, while listening on a root-owned socket (`/run/systemd/journal/syslog`) use
socket activation
socket activation. Create a unit in `/etc/systemd/system/circolog.service`:
[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,5 +4,6 @@ nav:
- Install: install.md
- Queries: query.md
- Hacking: hacking.md
- Systemd: systemd.md
repo_url: https://git.lattuga.net/boyska/circolog
repo_name: 'Repository'

@ -1 +1 @@
Subproject commit cf9010332939e9bd80638ef5db53c5547ca30588
Subproject commit 166aad3f993ce4a67bf486e62d637c834c8a8fe6