mirror of
				https://github.com/quitesimpleorg/hs9001.git
				synced 2025-11-04 14:09:30 +01:00 
			
		
		
		
	Compare commits
	
		
			8 Commits
		
	
	
		
			v0.2
			...
			a8f35951f1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					a8f35951f1 | ||
| 
						 | 
					b9e3a3629b | ||
| 
						 | 
					16752411e1 | ||
| 
						 | 
					c480519fca | ||
| 
						 | 
					70e66f47ba | ||
| 216e59747c | |||
| c206b07b2d | |||
| 3b01d7b898 | 
							
								
								
									
										6
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -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}"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -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
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								main.go
									
									
									
									
									
								
							@@ -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()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user