1
0
miroir de https://github.com/quitesimpleorg/hs9001.git synchronisé 2025-07-17 21:14:56 +02:00

8 Révisions

Auteur SHA1 Message Date
a8f35951f1 Replace previous PROMPT_COMMAND in bashrc with more convenient option 2021-06-05 15:42:33 +02:00
b9e3a3629b Fix bug in cwd search and fix go-staticcheck regexp complaint 2021-06-05 15:05:18 +02:00
16752411e1 Add 'nolog' subcommand
The nolog subcommand instructs the user on how to disable logging
for the current shell. This is not (easily) doable automatically
since we can't affect a parent's environment without insane hacks.

Fixes #4
2021-06-05 14:49:25 +02:00
c480519fca Resolve CWD arguments as absolute path
Previously when calling e.g 'hs9001 search -cwd .' we would search
for a litteral '.' in the database. Now we resolve that argument
relative to the CWD.
2021-06-05 14:28:02 +02:00
70e66f47ba CLI: Rename -workdir to -cwd so we have to type less 2021-06-05 14:27:25 +02:00
216e59747c Add 'version' command 2021-05-16 18:38:50 +02:00
c206b07b2d add/search: Process cmd exit code 2021-05-16 18:01:48 +02:00
3b01d7b898 search: Do not print consecutive equal commands by default 2021-05-16 17:13:29 +02:00
3 fichiers modifiés avec 72 ajouts et 20 suppressions

6
Makefile Fichier normal
Voir le fichier

@ -0,0 +1,6 @@
GIT_COMMIT=$(shell git rev-list -1 HEAD)
GIT_TAG=$(shell git tag --sort="-version:refname" | head -n 1)
all:
go build -ldflags "-X main.GitCommit=${GIT_COMMIT} -X main.GitTag=${GIT_TAG}"

Voir le fichier

@ -34,9 +34,7 @@ apk add hs9001
Add this to .bashrc
```
if [ -n "$PS1" ] ; then
PROMPT_COMMAND='hs9001 add -ret $? "$(history 1)"'
fi
eval "$(hs9001 bash-enable)"
```
By default, every system user gets his own database. You can override this by setting the environment variable
for all users that should write to your unified database.

82
main.go
Voir le fichier

@ -23,9 +23,13 @@ type HistoryEntry struct {
cwd string
hostname string
user string
retval int
timestamp time.Time
}
var GitTag string
var GitCommit string
func databaseLocation() string {
envOverride := os.Getenv("HS9001_DB_PATH")
if envOverride != "" {
@ -57,7 +61,8 @@ func initDatabase(conn *sql.DB) {
func migrateDatabase(conn *sql.DB, currentVersion int) {
migrations := []string{
"ALTER TABLE history add column workdir varchar(4096) DEFAULT ''",
"ALTER TABLE history ADD COLUMN workdir varchar(4096) DEFAULT ''",
"ALTER TABLE history ADD COLUMN retval integer DEFAULT -9001",
}
if !(len(migrations) > currentVersion) {
@ -103,7 +108,7 @@ func setDBVersion(conn *sql.DB, ver int) {
}
}
func NewHistoryEntry(cmd string) HistoryEntry {
func NewHistoryEntry(cmd string, retval int) HistoryEntry {
wd, err := os.Getwd()
if err != nil {
log.Panic(err)
@ -118,6 +123,7 @@ func NewHistoryEntry(cmd string) HistoryEntry {
cmd: cmd,
cwd: wd,
timestamp: time.Now(),
retval: retval,
}
}
@ -130,7 +136,7 @@ func importFromStdin(conn *sql.DB) {
}
for scanner.Scan() {
entry := NewHistoryEntry(scanner.Text())
entry := NewHistoryEntry(scanner.Text(), -9001)
entry.cwd = ""
entry.timestamp = time.Unix(0, 0)
add(conn, entry)
@ -142,10 +148,29 @@ func importFromStdin(conn *sql.DB) {
}
}
func search(conn *sql.DB, q string, workdir string, beginTime time.Time, endTime time.Time) list.List {
queryStmt := "SELECT id, command, workdir, user, hostname FROM history WHERE timestamp BETWEEN datetime(?, 'unixepoch') AND datetime(?, 'unixepoch') AND command LIKE ? AND workdir LIKE ? ORDER BY timestamp ASC"
func search(conn *sql.DB, q string, workdir string, beginTime time.Time, endTime time.Time, retval int) list.List {
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, beginTime.Unix(), endTime.Unix(), q, workdir)
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 {
log.Panic(err)
}
@ -154,7 +179,7 @@ func search(conn *sql.DB, q string, workdir string, beginTime time.Time, endTime
defer rows.Close()
for rows.Next() {
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 {
log.Panic(err)
}
@ -173,12 +198,12 @@ func delete(conn *sql.DB, entryId uint32) {
}
func add(conn *sql.DB, entry HistoryEntry) {
stmt, err := conn.Prepare("INSERT INTO history (user, command, hostname, workdir, timestamp) VALUES (?, ?, ?, ?, datetime(?, 'unixepoch'))")
stmt, err := conn.Prepare("INSERT INTO history (user, command, hostname, workdir, timestamp, retval) VALUES (?, ?, ?, ?, datetime(?, 'unixepoch'),?)")
if err != nil {
log.Panic(err)
}
_, err = stmt.Exec(entry.user, entry.cmd, entry.hostname, entry.cwd, entry.timestamp.Unix())
_, err = stmt.Exec(entry.user, entry.cmd, entry.hostname, entry.cwd, entry.timestamp.Unix(), entry.retval)
if err != nil {
log.Panic(err)
}
@ -209,7 +234,7 @@ func exists(path string) (bool, error) {
}
func printUsage() {
fmt.Fprintf(os.Stderr, "Usage: ./hs9001 <add/search/import>\n")
fmt.Fprintf(os.Stderr, "Usage: ./hs9001 <add/search/import/nolog/bash-enable>\n")
}
func main() {
@ -241,6 +266,14 @@ func main() {
migrateDatabase(conn, fetchDBVersion(conn))
switch cmd {
case "bash-enable":
fmt.Printf(`
if [ -n "$PS1" ] ; then
PROMPT_COMMAND='hs9001 add -ret $? "$(history 1)"'
fi
`)
case "bash-disable":
fmt.Printf("unset PROMPT_COMMAND\n")
case "add":
var ret int
addCmd.IntVar(&ret, "ret", 0, "Return value of the command to add")
@ -255,10 +288,10 @@ func main() {
}
historycmd := args[0]
var rgx = regexp.MustCompile("\\s+\\d+\\s+(.*)")
var rgx = regexp.MustCompile(`\s+\d+\s+(.*)`)
rs := rgx.FindStringSubmatch(historycmd)
if len(rs) == 2 {
add(conn, NewHistoryEntry(rs[1]))
add(conn, NewHistoryEntry(rs[1], ret))
}
case "search":
fallthrough
@ -266,10 +299,13 @@ func main() {
var workDir string
var beginTime string
var endTime string
searchCmd.StringVar(&workDir, "workdir", "%", "Search only within this workdir")
var distinct bool = true
var retVal int
searchCmd.StringVar(&workDir, "cwd", "%", "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)
@ -286,15 +322,25 @@ func main() {
}
q := strings.Join(args, " ")
results := search(conn, "%"+q+"%", workDir, beginTimestamp, endTimeStamp)
if workDir != "%" {
workDir, err = filepath.Abs(workDir)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed parse working directory path: %s\n", err.Error())
}
}
results := search(conn, "%"+q+"%", workDir, beginTimestamp, endTimeStamp, retVal)
previousCmd := ""
for e := results.Front(); e != nil; e = e.Next() {
entry, ok := e.Value.(*HistoryEntry)
if !ok {
log.Panic("Failed to retrieve entries")
}
fmt.Printf("%s\n", entry.cmd)
if !distinct || previousCmd != entry.cmd {
fmt.Printf("%s\n", entry.cmd)
}
previousCmd = entry.cmd
}
if cmd == "delete" {
@ -326,6 +372,8 @@ func main() {
os.Exit(23)
case "import":
importFromStdin(conn)
case "version":
fmt.Fprintf(os.Stdout, "Git Tag: %s\nGit Commit: %s\n", GitTag, GitCommit)
default:
fmt.Fprintf(os.Stderr, "Error: Unknown subcommand '%s' supplied\n\n", cmd)
printUsage()