// Copyright 2021, johan@nosd.in //go:build freebsd // +build freebsd // // godit is a search tool for BSM audit trails used by FreeBSD auditd // /* % time praudit -l /home/yo/Dev/go/godit/20211228134923.20211228151348 > praudit.log 101.728u 7.315s 1:49.09 99.9% 10+167k 0+191152io 0pf+0w % time ./godit 20211228134923.20211228151348 > godit.log 11.599u 38.235s 0:48.25 103.2% 1045+553k 1+2262168io 4pf+0w % ./godit -V Godit v0.03 % time ./godit 20211228134923.20211228151348 > 20211228134923.20211228151348.godit3 7.183u 19.590s 0:25.98 103.0% 1038+559k 0+2262168io 0pf+0w % ./godit -V Godit v0.4.3 */ package main import ( "io" "os" "fmt" "sync" "bufio" "strings" "syscall" "os/signal" "github.com/spf13/pflag" ) const ( version = "0.6.3" ) var ( randFlag bool showVersion bool // Default delimiter delimiter = "," Writer *bufio.Writer ) func NewWriter(file string) (*bufio.Writer, *os.File, error) { if len(file) > 0 { var f *os.File var err error f, err = os.OpenFile(file, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0640) if err != nil { return nil, nil, err } Writer = bufio.NewWriter(f) return Writer, f, nil } else { Writer = bufio.NewWriter(os.Stdout) return Writer, nil, nil } } func main() { var flags int var oneLine bool var noUserResolve bool var syslog23 bool var json bool var outputFile string // Output file mutex var outfMtx sync.Mutex var outFile *os.File pflag.BoolVarP(&oneLine, "oneline", "l", false, "Prints the entire record on the same line") pflag.BoolVarP(&noUserResolve, "numeric", "n", false, "Do not convert user and group IDs to their names but leave in their numeric forms") pflag.BoolVarP(&json, "json", "j", false, "Print compact json") pflag.BoolVarP(&syslog23, "syslog23", "s", false, "Print time as \"2006-01-02T15:04:05.000Z07:00\", RFC339 with ms, also used on RSYSLOG_SyslogProtocol23Format. \"msec\" field will not be print in json output") pflag.StringVarP(&outputFile, "out", "o", "", "Output to file, overwrite existing. File will be re-opened receiving SIGUSR1.") pflag.BoolVarP(&showVersion, "version", "V", false, "Show version and exit") var Usage = func() { fmt.Fprintf(os.Stderr, "Usage of \"%s [opts] auditfile\":\n", os.Args[0]) pflag.PrintDefaults() fmt.Fprintf(os.Stderr, "Set auditfile to \"-\" to read stdin\n") } pflag.Usage = Usage pflag.Parse() if showVersion { fmt.Printf("Godit v%s\n", version) return } if oneLine { flags = flags + PRT_ONELINE } if noUserResolve { flags = flags + PRT_NORESOLVE_USER } if syslog23 { flags = flags + PRT_TIMESYSLOG23 } if json { flags |= PRT_JSON } args := os.Args if len(os.Args) < 2 { pflag.Usage() os.Exit(1) } filename := args[len(args)-1] // Get a writer, file or stdout _, outFile, err := NewWriter(outputFile) if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } if len(outputFile) > 0 { // Manage output file rotation when receiving SIGUSR1 sig := make(chan os.Signal) signal.Notify(sig, syscall.SIGUSR1) go func() { for { <-sig outfMtx.Lock() fmt.Println("SIGUSR1 received, recreating output file") outFile.Close() _, outFile, err = NewWriter(outputFile) if err != nil { outfMtx.Unlock() fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } outfMtx.Unlock() } }() } var f *os.File var r *bufio.Reader if len(filename) > 0 { // If arg is "-", open stdin to read content if true == strings.EqualFold(filename, "-") { r = bufio.NewReader(os.Stdin) } else { f, err = os.Open(filename) if err != nil { fmt.Fprintf(os.Stderr, "Impossible d'ouvrir le fichier %s\n", filename) os.Exit(-1) } r = bufio.NewReader(f) } for { rec, err := readRecordToStruct(r) if err != nil { if err != io.EOF { fmt.Printf("Erreur : %v\n", err) } else { // v.0.4.2 : Continue on error return } } if len(outputFile) > 0 { outfMtx.Lock() rec.Print(Writer, ",", flags) Writer.Flush() // Performance ? outfMtx.Unlock() } else { // No need for mutex with stdout rec.Print(Writer, ",", flags) } } } if len(outputFile) > 0 && outFile != nil { outfMtx.Lock() outFile.Close() outfMtx.Unlock() } }