Compare commits
6 commits
3d463823e3
...
f7caefdae1
Author | SHA1 | Date | |
---|---|---|---|
f7caefdae1 | |||
b0cd779d61 | |||
7937d8b4c3 | |||
3e27cad5b1 | |||
a2de164a10 | |||
9728b236ed |
7 changed files with 150 additions and 15 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.*.vim
|
39
cmd/circologd/activation.go
Normal file
39
cmd/circologd/activation.go
Normal 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
|
||||
}
|
|
@ -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,13 +57,24 @@ 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 {
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
syslogSocketPath := syslogSocket.Path
|
||||
if syslogSocketPath != "" {
|
||||
if err = server.ListenUnixgram(syslogSocketPath); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "argh", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer cleanSocket(*syslogSocketPath)
|
||||
fmt.Printf("Binding socket `%s` [syslog]\n", *syslogSocketPath)
|
||||
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 {
|
||||
|
@ -64,6 +82,7 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err = server.Boot(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "argh", err)
|
||||
os.Exit(1)
|
||||
|
@ -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
50
cmd/circologd/sockets.go
Normal 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
|
||||
}
|
|
@ -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`.
|
||||
|
|
|
@ -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'
|
||||
|
|
2
vendor/gopkg.in/mcuadros/go-syslog.v2
generated
vendored
2
vendor/gopkg.in/mcuadros/go-syslog.v2
generated
vendored
|
@ -1 +1 @@
|
|||
Subproject commit cf9010332939e9bd80638ef5db53c5547ca30588
|
||||
Subproject commit 166aad3f993ce4a67bf486e62d637c834c8a8fe6
|
Loading…
Reference in a new issue