From 0c19f942927e3534052deaeec84cb152e9c0d869 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Tue, 24 Mar 2026 08:37:08 -0700 Subject: [PATCH] M7: add verbose mode for debugging Add -v/--verbose persistent flag that prints debug info to stderr: D-Bus connection status, token plugin directory discovery, unlock method sequencing with per-method success/failure, and full cryptsetup command lines including LD_LIBRARY_PATH. Co-Authored-By: Claude Opus 4.6 (1M context) --- cmd/root.go | 5 +++++ internal/cryptsetup/cryptsetup.go | 8 ++++++++ internal/udisks/client.go | 2 ++ internal/unlock/unlock.go | 5 +++++ internal/verbose/verbose.go | 16 ++++++++++++++++ 5 files changed, 36 insertions(+) create mode 100644 internal/verbose/verbose.go diff --git a/cmd/root.go b/cmd/root.go index b3f14b1..687a8d1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "git.wntrmute.dev/kyle/arca/internal/verbose" "github.com/spf13/cobra" ) @@ -12,6 +13,10 @@ var rootCmd = &cobra.Command{ Short: "Mount and unmount LUKS-encrypted volumes", } +func init() { + rootCmd.PersistentFlags().BoolVarP(&verbose.Enabled, "verbose", "v", false, "print debug information to stderr") +} + func SetVersion(v string) { rootCmd.Version = v } diff --git a/internal/cryptsetup/cryptsetup.go b/internal/cryptsetup/cryptsetup.go index 5ede88b..13c1e6e 100644 --- a/internal/cryptsetup/cryptsetup.go +++ b/internal/cryptsetup/cryptsetup.go @@ -5,6 +5,9 @@ import ( "os" "os/exec" "path/filepath" + "strings" + + "git.wntrmute.dev/kyle/arca/internal/verbose" ) // Open opens a LUKS device using cryptsetup with token-based unlock. @@ -12,6 +15,8 @@ func Open(devicePath, mapperName string) error { args := withTokenPluginEnv([]string{"cryptsetup", "open", devicePath, mapperName, "--token-only"}) args = withPrivilege(args) + verbose.Printf("exec: %s", strings.Join(args, " ")) + cmd := exec.Command(args[0], args[1:]...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout @@ -101,6 +106,7 @@ func findTokenPluginDir() string { // NixOS stable symlink — survives rebuilds. const nixSystemPath = "/run/current-system/sw/lib/cryptsetup" if hasTokenPlugins(nixSystemPath) { + verbose.Printf("token plugin dir: %s", nixSystemPath) return nixSystemPath } @@ -109,11 +115,13 @@ func findTokenPluginDir() string { if resolved, err := filepath.EvalSymlinks(bin); err == nil { dir := filepath.Join(filepath.Dir(filepath.Dir(resolved)), "lib", "cryptsetup") if hasTokenPlugins(dir) { + verbose.Printf("token plugin dir (from systemd-cryptenroll): %s", dir) return dir } } } + verbose.Printf("no token plugin directory found") return "" } diff --git a/internal/udisks/client.go b/internal/udisks/client.go index fefdb30..76f1dab 100644 --- a/internal/udisks/client.go +++ b/internal/udisks/client.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" + "git.wntrmute.dev/kyle/arca/internal/verbose" "github.com/godbus/dbus/v5" ) @@ -18,6 +19,7 @@ func NewClient() (*Client, error) { if err != nil { return nil, fmt.Errorf("cannot connect to udisks2 — is the udisks2 service running? (%w)", err) } + verbose.Printf("connected to system D-Bus") return &Client{conn: conn}, nil } diff --git a/internal/unlock/unlock.go b/internal/unlock/unlock.go index d5316b2..20b12d7 100644 --- a/internal/unlock/unlock.go +++ b/internal/unlock/unlock.go @@ -8,6 +8,7 @@ import ( "git.wntrmute.dev/kyle/arca/internal/cryptsetup" "git.wntrmute.dev/kyle/arca/internal/udisks" + "git.wntrmute.dev/kyle/arca/internal/verbose" ) // Result holds the outcome of a successful unlock. @@ -35,12 +36,16 @@ func New(client *udisks.Client, opts Options) *Unlocker { // Unlock tries each method in order and returns the result on first success. func (u *Unlocker) Unlock(dev *udisks.BlockDevice, methods []string) (*Result, error) { + verbose.Printf("unlock %s: methods %v", dev.DevicePath, methods) var errs []error for _, method := range methods { + verbose.Printf("trying method: %s", method) res, err := u.tryMethod(dev, method) if err == nil { + verbose.Printf("method %s succeeded", method) return res, nil } + verbose.Printf("method %s failed: %v", method, err) errs = append(errs, fmt.Errorf("%s: %w", method, err)) } return nil, fmt.Errorf("all unlock methods failed for %s:\n%w", dev.DevicePath, errors.Join(errs...)) diff --git a/internal/verbose/verbose.go b/internal/verbose/verbose.go new file mode 100644 index 0000000..d61ee4d --- /dev/null +++ b/internal/verbose/verbose.go @@ -0,0 +1,16 @@ +package verbose + +import ( + "fmt" + "os" +) + +// Enabled is set by the root command's --verbose flag. +var Enabled bool + +// Printf prints to stderr if verbose mode is enabled. +func Printf(format string, args ...any) { + if Enabled { + fmt.Fprintf(os.Stderr, "arca: "+format+"\n", args...) + } +}