config check: validates UUID format, recognized methods, keyfile consistency and existence. Reports all issues with alias context. remove: deletes a device from config by alias. Inverse of add. status: --mounted, --unlocked, --locked flags filter the device table. Flags combine as OR. mount/unlock: display which method succeeded and key slot used, e.g. "(fido2, key slot 1)". cryptsetup Open now runs with -v and parses "Key slot N unlocked" from stderr via io.MultiWriter. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
88 lines
1.8 KiB
Go
88 lines
1.8 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"text/tabwriter"
|
|
|
|
"git.wntrmute.dev/kyle/arca/internal/config"
|
|
"git.wntrmute.dev/kyle/arca/internal/udisks"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
filterMounted bool
|
|
filterUnlocked bool
|
|
filterLocked bool
|
|
)
|
|
|
|
var statusCmd = &cobra.Command{
|
|
Use: "status",
|
|
Short: "Show LUKS volume status",
|
|
RunE: runStatus,
|
|
}
|
|
|
|
func init() {
|
|
statusCmd.Flags().BoolVar(&filterMounted, "mounted", false, "show only mounted devices")
|
|
statusCmd.Flags().BoolVar(&filterUnlocked, "unlocked", false, "show only unlocked (but not mounted) devices")
|
|
statusCmd.Flags().BoolVar(&filterLocked, "locked", false, "show only locked devices")
|
|
rootCmd.AddCommand(statusCmd)
|
|
}
|
|
|
|
func runStatus(cmd *cobra.Command, args []string) error {
|
|
cfg := config.Load()
|
|
|
|
client, err := udisks.NewClient()
|
|
if err != nil {
|
|
return fmt.Errorf("connecting to udisks2: %w", err)
|
|
}
|
|
defer client.Close()
|
|
|
|
devices, err := client.ListEncryptedDevices()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
filtering := filterMounted || filterUnlocked || filterLocked
|
|
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0)
|
|
fmt.Fprintln(w, "DEVICE\tUUID\tALIAS\tSTATE\tMOUNTPOINT")
|
|
|
|
for _, dev := range devices {
|
|
alias := cfg.AliasFor(dev.UUID)
|
|
state := "locked"
|
|
mountpoint := ""
|
|
|
|
if ct, err := client.CleartextDevice(&dev); err == nil {
|
|
state = "unlocked"
|
|
if mp, err := client.MountPoint(ct); err == nil && mp != "" {
|
|
mountpoint = mp
|
|
state = "mounted"
|
|
}
|
|
}
|
|
|
|
if filtering {
|
|
switch state {
|
|
case "mounted":
|
|
if !filterMounted {
|
|
continue
|
|
}
|
|
case "unlocked":
|
|
if !filterUnlocked {
|
|
continue
|
|
}
|
|
case "locked":
|
|
if !filterLocked {
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
|
|
dev.DevicePath, dev.UUID, alias, state, mountpoint)
|
|
}
|
|
|
|
w.Flush()
|
|
return nil
|
|
}
|