diff --git a/.gitignore b/.gitignore index 38ae3d2..9afef4e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /ctool +/ctfetch +/ctail diff --git a/cmd/ctool/cmd_fetch.go b/cmd/ctool/cmd_fetch.go index 3207bfe..9e62ebf 100644 --- a/cmd/ctool/cmd_fetch.go +++ b/cmd/ctool/cmd_fetch.go @@ -18,12 +18,13 @@ func runFetch(args []string) { logsListURL := fs.String("logs-list-url", "https://www.gstatic.com/ct/log_list/v3/all_logs_list.json", "URL of the CT log list JSON") monitoringURL := fs.String("monitoring-url", "", "log root URL for issuer lookups when input is a file") fs.Usage = func() { + n := cmdName("fetch") fmt.Fprintf(os.Stderr, "Usage:\n") - fmt.Fprintf(os.Stderr, " ctool fetch [flags] [+sct] [+issuer] [+ctlog] [+all] fetch one entry\n") - fmt.Fprintf(os.Stderr, " ctool fetch [flags] [+sct] [+issuer] [+ctlog] [+all] dump all entries in a tile\n") + fmt.Fprintf(os.Stderr, " %s [flags] [+sct] [+issuer] [+ctlog] [+all] fetch one entry\n", n) + fmt.Fprintf(os.Stderr, " %s [flags] [+sct] [+issuer] [+ctlog] [+all] dump all entries in a tile\n", n) fmt.Fprintf(os.Stderr, "\nExamples:\n") - fmt.Fprintf(os.Stderr, " ctool fetch https://halloumi2026h1.mon.ct.ipng.ch 629794635 +all\n") - fmt.Fprintf(os.Stderr, " ctool fetch https://halloumi2026h1.mon.ct.ipng.ch/tile/data/x002/x460/135 +sct\n") + fmt.Fprintf(os.Stderr, " %s https://halloumi2026h1.mon.ct.ipng.ch 629794635 +all\n", n) + fmt.Fprintf(os.Stderr, " %s https://halloumi2026h1.mon.ct.ipng.ch/tile/data/x002/x460/135 +sct\n", n) fmt.Fprintf(os.Stderr, "\nFlags:\n") fs.PrintDefaults() } diff --git a/cmd/ctool/cmd_tail.go b/cmd/ctool/cmd_tail.go index 95fd527..2bdf8d2 100644 --- a/cmd/ctool/cmd_tail.go +++ b/cmd/ctool/cmd_tail.go @@ -31,11 +31,12 @@ func runTail(args []string) { rateLimitFlag := fs.Duration("rate-limit", 2*time.Second, "minimum time between HTTP requests (minimum 100ms)") fs.StringVar(&userAgent, "user-agent", "ctool/"+version+" (https://git.ipng.ch/certificate-transparency/)", "User-Agent header for HTTP requests") fs.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: ctool tail [flags] \n") + n := cmdName("tail") + fmt.Fprintf(os.Stderr, "Usage: %s [flags] \n", n) fmt.Fprintf(os.Stderr, "\nPrints a one-liner per cert/pre-cert as new entries arrive in a Static CT log.\n") fmt.Fprintf(os.Stderr, "\nExamples:\n") - fmt.Fprintf(os.Stderr, " ctool tail https://halloumi2026h2.mon.ct.ipng.ch\n") - fmt.Fprintf(os.Stderr, " ctool tail --from-leaf 0 --interval 10s https://halloumi2026h2.mon.ct.ipng.ch\n") + fmt.Fprintf(os.Stderr, " %s https://halloumi2026h2.mon.ct.ipng.ch\n", n) + fmt.Fprintf(os.Stderr, " %s --from-leaf 0 --interval 10s https://halloumi2026h2.mon.ct.ipng.ch\n", n) fmt.Fprintf(os.Stderr, "\nFlags:\n") fs.PrintDefaults() } diff --git a/cmd/ctool/main.go b/cmd/ctool/main.go index 176d1b3..dc5e806 100644 --- a/cmd/ctool/main.go +++ b/cmd/ctool/main.go @@ -8,11 +8,23 @@ package main import ( "fmt" "os" + "path/filepath" + "strings" ) const version = "0.1.0" func main() { + // Busybox-style: if invoked as "ctfetch" or "ctail", shortcut to that command. + switch strings.TrimSuffix(filepath.Base(os.Args[0]), filepath.Ext(os.Args[0])) { + case "ctfetch": + runFetch(os.Args[1:]) + return + case "ctail": + runTail(os.Args[1:]) + return + } + if len(os.Args) < 2 { usage() os.Exit(1) @@ -42,3 +54,14 @@ func fatal(format string, args ...any) { fmt.Fprintf(os.Stderr, "Error: "+format+"\n", args...) os.Exit(1) } + +// cmdName returns the effective command name for usage messages. +// When invoked via a symlink (e.g. "ctfetch"), it returns that name directly. +// Otherwise it returns "ctool " (e.g. "ctool fetch"). +func cmdName(sub string) string { + base := strings.TrimSuffix(filepath.Base(os.Args[0]), filepath.Ext(os.Args[0])) + if base == "ctool" { + return "ctool " + sub + } + return base +}