1
0
spogulis no https://github.com/quitesimpleorg/hs9001.git synced 2025-07-16 15:14:57 +02:00

6 Revīzijas

3 mainīti faili ar 94 papildinājumiem un 28 dzēšanām

5
go.mod
Parādīt failu

@ -2,4 +2,7 @@ module hs9001
go 1.16 go 1.16
require modernc.org/sqlite v1.10.0 require (
github.com/tj/go-naturaldate v1.3.0
modernc.org/sqlite v1.10.0
)

15
go.sum
Parādīt failu

@ -1,3 +1,5 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo= github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo=
@ -8,8 +10,17 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tj/assert v0.0.0-20190920132354-ee03d75cd160 h1:NSWpaDaurcAJY7PkL8Xt0PhZE7qpvbZl5ljd8r6U0bI=
github.com/tj/assert v0.0.0-20190920132354-ee03d75cd160/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
github.com/tj/go-naturaldate v1.3.0 h1:OgJIPkR/Jk4bFMBLbxZ8w+QUxwjqSvzd9x+yXocY4RI=
github.com/tj/go-naturaldate v1.3.0/go.mod h1:rpUbjivDKiS1BlfMGc2qUKNZ/yxgthOfmytQs8d8hKk=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -39,6 +50,10 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009 h1:u0oCo5b9wyLr++HF3AN9JicGhkUxJhMz51+8TIZH9N0= modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009 h1:u0oCo5b9wyLr++HF3AN9JicGhkUxJhMz51+8TIZH9N0=
modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878=
modernc.org/ccgo/v3 v3.9.0 h1:JbcEIqjw4Agf+0g3Tc85YvfYqkkFOv6xBwS4zkfqSoA= modernc.org/ccgo/v3 v3.9.0 h1:JbcEIqjw4Agf+0g3Tc85YvfYqkkFOv6xBwS4zkfqSoA=

102
main.go
Parādīt failu

@ -11,16 +11,20 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"time"
"github.com/tj/go-naturaldate"
_ "modernc.org/sqlite" _ "modernc.org/sqlite"
) )
type HistoryEntry struct { type HistoryEntry struct {
id uint32 id uint32
cmd string cmd string
cwd string cwd string
hostname string hostname string
user string user string
retval int
timestamp time.Time
} }
func databaseLocation() string { func databaseLocation() string {
@ -54,7 +58,8 @@ func initDatabase(conn *sql.DB) {
func migrateDatabase(conn *sql.DB, currentVersion int) { func migrateDatabase(conn *sql.DB, currentVersion int) {
migrations := []string{ migrations := []string{
"ALTER TABLE history add column workdir varchar(4096)", "ALTER TABLE history ADD COLUMN workdir varchar(4096) DEFAULT ''",
"ALTER TABLE history ADD COLUMN retval integer DEFAULT -9001",
} }
if !(len(migrations) > currentVersion) { if !(len(migrations) > currentVersion) {
@ -100,7 +105,7 @@ func setDBVersion(conn *sql.DB, ver int) {
} }
} }
func NewHistoryEntry(cmd string) HistoryEntry { func NewHistoryEntry(cmd string, retval int) HistoryEntry {
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
@ -110,10 +115,12 @@ func NewHistoryEntry(cmd string) HistoryEntry {
log.Panic(err) log.Panic(err)
} }
return HistoryEntry{ return HistoryEntry{
user : os.Getenv("USER"), user: os.Getenv("USER"),
hostname : hostname, hostname: hostname,
cmd : cmd, cmd: cmd,
cwd : wd, cwd: wd,
timestamp: time.Now(),
retval: retval,
} }
} }
@ -126,8 +133,9 @@ func importFromStdin(conn *sql.DB) {
} }
for scanner.Scan() { for scanner.Scan() {
entry := NewHistoryEntry(scanner.Text()) entry := NewHistoryEntry(scanner.Text(), -9001)
entry.cwd = "" entry.cwd = ""
entry.timestamp = time.Unix(0, 0)
add(conn, entry) add(conn, entry)
} }
@ -137,10 +145,29 @@ func importFromStdin(conn *sql.DB) {
} }
} }
func search(conn *sql.DB, q string) list.List { func search(conn *sql.DB, q string, workdir string, beginTime time.Time, endTime time.Time, retval int) list.List {
queryStmt := "SELECT id, command, workdir, user, hostname FROM history WHERE command LIKE ? ORDER BY timestamp ASC" var sb strings.Builder
sb.WriteString("SELECT id, command, workdir, user, hostname, retval ")
sb.WriteString("FROM history ")
sb.WriteString("WHERE timestamp BETWEEN datetime(?, 'unixepoch') ")
sb.WriteString("AND datetime(?, 'unixepoch') ")
sb.WriteString("AND command LIKE ? ")
sb.WriteString("AND workdir LIKE ? ")
if retval != -9001 {
sb.WriteString("AND retval = ? ")
}
sb.WriteString("ORDER BY timestamp ASC ")
rows, err := conn.Query(queryStmt, "%"+q+"%") queryStmt := sb.String()
args := make([]interface{}, 0)
args = append(args, beginTime.Unix())
args = append(args, endTime.Unix())
args = append(args, q)
args = append(args, workdir)
if retval != -9001 {
args = append(args, retval)
}
rows, err := conn.Query(queryStmt, args...)
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
@ -149,7 +176,7 @@ func search(conn *sql.DB, q string) list.List {
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var entry HistoryEntry var entry HistoryEntry
err = rows.Scan(&entry.id, &entry.cmd, &entry.cwd, &entry.user, &entry.hostname) err = rows.Scan(&entry.id, &entry.cmd, &entry.cwd, &entry.user, &entry.hostname, &entry.retval)
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
@ -167,13 +194,13 @@ func delete(conn *sql.DB, entryId uint32) {
} }
} }
func add(conn *sql.DB, entry HistoryEntry ) { func add(conn *sql.DB, entry HistoryEntry) {
stmt, err := conn.Prepare("INSERT INTO history (user, command, hostname, workdir) VALUES (?, ?, ?, ?)") stmt, err := conn.Prepare("INSERT INTO history (user, command, hostname, workdir, timestamp, retval) VALUES (?, ?, ?, ?, datetime(?, 'unixepoch'),?)")
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
_, err = stmt.Exec(entry.user, entry.cmd, entry.hostname, entry.cwd) _, err = stmt.Exec(entry.user, entry.cmd, entry.hostname, entry.cwd, entry.timestamp.Unix(), entry.retval)
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
@ -253,28 +280,49 @@ func main() {
var rgx = regexp.MustCompile("\\s+\\d+\\s+(.*)") var rgx = regexp.MustCompile("\\s+\\d+\\s+(.*)")
rs := rgx.FindStringSubmatch(historycmd) rs := rgx.FindStringSubmatch(historycmd)
if len(rs) == 2 { if len(rs) == 2 {
add(conn, NewHistoryEntry(rs[1])) add(conn, NewHistoryEntry(rs[1], ret))
} }
case "search": fallthrough; case "search":
fallthrough
case "delete": case "delete":
var workDir string
var beginTime string
var endTime string
var distinct bool = true
var retVal int
searchCmd.StringVar(&workDir, "workdir", "%", "Search only within this workdir")
searchCmd.StringVar(&beginTime, "begin", "50 years ago", "Start searching from this timeframe")
searchCmd.StringVar(&endTime, "end", "now", "End searching from this timeframe")
searchCmd.BoolVar(&distinct, "distinct", true, "Remove consecutive duplicate commands from output")
searchCmd.IntVar(&retVal, "ret", -9001, "Only query commands that returned with this exit code. -9001=all (default)")
searchCmd.Parse(globalargs) searchCmd.Parse(globalargs)
args := searchCmd.Args() args := searchCmd.Args()
if len(args) < 1 { beginTimestamp, err := naturaldate.Parse(beginTime, time.Now())
fmt.Fprint(os.Stderr, "Please provide the query\n") if err != nil {
os.Exit(1) fmt.Fprintf(os.Stderr, "Failed to convert time string: %s\n", err.Error())
}
endTimeStamp, err := naturaldate.Parse(endTime, time.Now())
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to convert time string: %s\n", err.Error())
} }
q := strings.Join(args, " ") q := strings.Join(args, " ")
results := search(conn, q) results := search(conn, "%"+q+"%", workDir, beginTimestamp, endTimeStamp, retVal)
previousCmd := ""
for e := results.Front(); e != nil; e = e.Next() { for e := results.Front(); e != nil; e = e.Next() {
entry, ok := e.Value.(*HistoryEntry) entry, ok := e.Value.(*HistoryEntry)
if !ok { if !ok {
log.Panic("Failed to retrieve entries") log.Panic("Failed to retrieve entries")
} }
if !distinct || previousCmd != entry.cmd {
fmt.Printf("%s\n", entry.cmd) fmt.Printf("%s\n", entry.cmd)
}
previousCmd = entry.cmd
} }
if cmd == "delete" { if cmd == "delete" {