2018-08-22 18:47:00 +02:00
package main
import (
"flag"
"fmt"
2018-11-10 17:41:45 +01:00
"log"
2018-08-23 02:08:53 +02:00
"net"
2018-08-22 18:47:00 +02:00
"net/http"
2018-08-22 23:51:59 +02:00
"os"
2018-11-10 16:32:12 +01:00
"os/signal"
2018-11-10 17:41:45 +01:00
"syscall"
2019-01-07 10:06:14 +01:00
"time"
2018-08-22 18:47:00 +02:00
2018-08-23 01:21:53 +02:00
"git.lattuga.net/boyska/circolog"
2019-05-01 15:55:41 +02:00
"git.lattuga.net/boyska/circolog/formatter"
2019-01-07 10:06:14 +01:00
"github.com/coreos/go-systemd/daemon"
2018-08-22 18:47:00 +02:00
syslog "gopkg.in/mcuadros/go-syslog.v2"
)
2019-05-03 10:32:18 +02:00
var socketsToRemove [ ] string
2018-11-10 16:32:12 +01:00
func cleanSocket ( socket string ) {
if err := os . Remove ( socket ) ; err != nil {
2018-11-11 19:51:21 +01:00
fmt . Fprintln ( os . Stderr , "Error cleaning" , socket , ":" , err )
2018-11-10 16:32:12 +01:00
}
}
2019-05-03 10:32:18 +02:00
func removeAtExit ( socket string ) {
socketsToRemove = append ( socketsToRemove , socket )
}
2018-08-22 18:47:00 +02:00
func main ( ) {
2018-08-22 23:51:59 +02:00
var err error
2019-05-03 10:45:35 +02:00
var syslogSocket SyslogSocket
2019-05-01 15:55:41 +02:00
var logFmt formatter . SyslogRFC
logFmt . Format = syslog . Automatic
2019-05-03 10:45:35 +02:00
flag . Var ( & syslogSocket , "syslogd-socket" , "The socket to listen to syslog addresses" )
2018-08-22 18:47:00 +02:00
// 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" )
2018-08-22 22:59:32 +02:00
syslogAddr := flag . String ( "syslog-addr" , "127.0.0.1:9514" , "Address:port where to listen for syslog messages" )
2019-05-28 18:42:20 +02:00
queryAddr := flag . String ( "query-addr" , "" , "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!" )
2018-11-11 19:51:21 +01:00
ctlSocket := flag . String ( "ctl-socket" , "/tmp/circologd-ctl.sock" , "Path to a unix domain socket for the control server; leave empty to disable" )
2019-05-01 15:55:41 +02:00
flag . Var ( & logFmt , "log-fmt" , "Log messages format. If not set, defaults to automatic choice. Allowed values: rfc3164, rfc5424, auto." )
2018-12-20 09:45:05 +01:00
verbose := flag . Bool ( "verbose" , false , "Print more output executing the daemon" )
2018-12-24 18:41:06 +01:00
debug := flag . Bool ( "debug" , false , "Print debugging info executing the daemon" )
2018-08-22 18:47:00 +02:00
flag . Parse ( )
2018-11-10 16:32:12 +01:00
interrupt := make ( chan os . Signal , 1 )
2018-12-05 09:18:40 +01:00
signal . Notify ( interrupt , syscall . SIGINT , syscall . SIGUSR1 , syscall . SIGUSR2 , syscall . SIGTERM )
2018-11-10 16:32:12 +01:00
2018-08-23 01:21:53 +02:00
hub := circolog . NewHub ( * bufsize )
2018-08-23 00:56:27 +02:00
handler := syslog . NewChannelHandler ( hub . LogMessages )
2018-11-10 17:50:51 +01:00
go hub . Run ( )
2018-08-22 18:47:00 +02:00
server := syslog . NewServer ( )
2019-05-01 15:55:41 +02:00
server . SetFormat ( logFmt . Format )
fmt . Printf ( "Syslog format set to: %s\n" , logFmt . String ( ) )
2018-08-22 18:47:00 +02:00
server . SetHandler ( handler )
2019-05-03 10:45:35 +02:00
if syslogSocket . isSocketActivated {
fmt . Printf ( "Binding to socket `%s` [syslog]\n" , syslogSocket . String ( ) )
2019-05-03 12:04:04 +02:00
if syslogSocket . Listener != nil {
fmt . Println ( "(stream)" )
server . Listen ( syslogSocket . Listener )
} else {
fmt . Println ( "(datagram)" , syslogSocket . Conn )
server . ListenDgram ( syslogSocket . Conn )
}
2018-08-22 20:56:43 +02:00
} else {
2019-05-03 10:45:35 +02:00
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 )
}
2018-08-22 23:51:59 +02:00
}
2018-08-22 18:47:00 +02:00
}
2018-08-22 23:51:59 +02:00
if err = server . Boot ( ) ; err != nil {
fmt . Fprintln ( os . Stderr , "argh" , err )
os . Exit ( 1 )
}
2018-08-22 18:47:00 +02:00
2018-11-11 19:51:21 +01:00
httpQueryServer := http . Server { Handler : setupHTTP ( hub ) }
2019-05-28 18:42:20 +02:00
if * queryAddr == "" {
2018-08-23 02:08:53 +02:00
fmt . Printf ( "Binding address `%s` [http]\n" , * querySocket )
unixListener , err := net . Listen ( "unix" , * querySocket )
if err != nil {
fmt . Fprintln ( os . Stderr , "Error binding HTTP unix domain socket" , err )
2018-11-10 17:28:02 +01:00
return
2018-08-23 02:08:53 +02:00
}
2019-05-03 10:32:18 +02:00
removeAtExit ( * querySocket )
2018-11-10 17:28:02 +01:00
go func ( ) {
2018-11-11 19:51:21 +01:00
if err := httpQueryServer . Serve ( unixListener ) ; err != nil && err != http . ErrServerClosed {
fmt . Fprintln ( os . Stderr , "error binding" , * querySocket , ":" , err )
2018-11-10 17:28:02 +01:00
}
} ( )
2018-08-23 02:08:53 +02:00
} else {
2018-11-11 19:51:21 +01:00
httpQueryServer . Addr = * queryAddr
2018-08-23 02:08:53 +02:00
fmt . Printf ( "Binding address `%s` [http]\n" , * queryAddr )
2018-11-10 17:28:02 +01:00
go func ( ) {
2018-11-11 19:51:21 +01:00
err := httpQueryServer . ListenAndServe ( )
if err != nil && err != http . ErrServerClosed {
fmt . Fprintln ( os . Stderr , "error binding" , * queryAddr , ":" , err )
}
} ( )
}
2018-12-24 18:41:06 +01:00
httpCtlServer := http . Server { Handler : setupHTTPCtl ( hub , * verbose , * debug ) }
2018-11-11 19:51:21 +01:00
if * ctlSocket != "" {
fmt . Printf ( "Binding address `%s` [http]\n" , * ctlSocket )
unixListener , err := net . Listen ( "unix" , * ctlSocket )
if err != nil {
fmt . Fprintln ( os . Stderr , "Error binding HTTP unix domain socket" , err )
return
}
2019-05-03 10:32:18 +02:00
removeAtExit ( * ctlSocket )
2018-11-11 19:51:21 +01:00
go func ( ) {
if err := httpCtlServer . Serve ( unixListener ) ; err != nil && err != http . ErrServerClosed {
2018-11-10 17:28:02 +01:00
fmt . Fprintln ( os . Stderr , "error binding:" , err )
}
} ( )
2018-11-10 16:32:12 +01:00
}
2018-11-11 19:51:21 +01:00
2019-01-07 10:06:14 +01:00
daemon . SdNotify ( false , daemon . SdNotifyReady )
2018-11-11 19:51:21 +01:00
2019-01-07 10:06:14 +01:00
var wdTick <- chan time . Time
if watchdogTime , err := daemon . SdWatchdogEnabled ( false ) ; err == nil && watchdogTime != 0 {
fmt . Println ( "systemd watchdog enabled" )
wdTick = time . Tick ( watchdogTime / 2 ) // much less than systemd default of 30s; TODO: make it configurable
}
2018-11-10 16:32:12 +01:00
for {
select {
2019-01-07 10:06:14 +01:00
case <- wdTick :
daemon . SdNotify ( false , daemon . SdNotifyWatchdog )
2018-11-10 17:41:45 +01:00
case sig := <- interrupt :
2018-11-10 18:21:42 +01:00
if sig == syscall . SIGUSR1 {
2018-12-24 15:54:22 +01:00
response := make ( chan circolog . CommandResponse )
hub . Commands <- circolog . HubFullCommand { Command : circolog . CommandPauseToggle , Response : response }
resp := <- response
2018-11-11 19:10:53 +01:00
if resp . Value . ( bool ) {
log . Println ( "resumed" )
} else {
log . Println ( "paused" )
}
2018-11-10 18:00:35 +01:00
}
2018-12-05 09:18:40 +01:00
if sig == syscall . SIGUSR2 {
2018-12-24 15:54:22 +01:00
response := make ( chan circolog . CommandResponse )
hub . Commands <- circolog . HubFullCommand { Command : circolog . CommandClear , Response : response }
resp := <- response
2018-12-05 09:18:40 +01:00
if resp . Value . ( bool ) {
log . Println ( "buffer cleaned" )
} else {
log . Println ( "buffer NOT cleaned" )
}
}
2018-11-10 18:00:35 +01:00
if sig == syscall . SIGTERM || sig == syscall . SIGINT {
log . Println ( "Quitting because of signal" , sig )
2019-01-07 10:06:14 +01:00
daemon . SdNotify ( false , daemon . SdNotifyStopping )
2018-11-10 18:00:35 +01:00
server . Kill ( )
2018-11-11 19:51:21 +01:00
if err := httpQueryServer . Shutdown ( nil ) ; err != nil {
2018-11-10 18:00:35 +01:00
fmt . Fprintln ( os . Stderr , "Error closing http server:" , err )
}
2018-11-11 19:51:21 +01:00
if err := httpCtlServer . Shutdown ( nil ) ; err != nil {
fmt . Fprintln ( os . Stderr , "Error closing control server:" , err )
}
2019-05-03 10:32:18 +02:00
for _ , socket := range socketsToRemove {
cleanSocket ( socket )
}
2018-11-10 18:00:35 +01:00
return
2018-11-10 17:36:17 +01:00
}
2018-11-10 16:32:12 +01:00
}
2018-08-23 02:08:53 +02:00
}
2018-08-22 18:47:00 +02:00
}