package main import ( "fmt" "os" "github.com/spf13/cobra" "git.wxccs.org/iceking2nd/winauth-go/internal/global" "git.wxccs.org/iceking2nd/winauth-go/internal/i18n" "git.wxccs.org/iceking2nd/winauth-go/internal/logging" "git.wxccs.org/iceking2nd/winauth-go/internal/ui" "git.wxccs.org/iceking2nd/winauth-go/internal/win32" ) const ( // singleInstanceMutex is the name used by AcquireInstanceLock. The // "Global\" prefix would make the lock cross-session; without it // each Windows user sees their own copy, which is what we want // (config.yaml is per-user anyway). singleInstanceMutex = "winauth-go-single-instance-mutex" // windowTitle is what AcquireInstanceLock's duplicate path tries to // foreground. Must stay in sync with ui.Run's app.Title option. windowTitle = "WinAuth" ) func main() { var ( console bool logLevel string logFile string configPath string ) root := &cobra.Command{ Use: "winauth-go", Short: "WinAuth (Go port) — TOTP/HOTP authenticator", RunE: func(cmd *cobra.Command, args []string) error { if console { logging.AttachConsole() } closer, err := logging.Init(logging.Options{ Level: logLevel, File: logFile, Console: console, }) if err != nil { return err } if closer != nil { defer func() { _ = closer.Close() }() } const fn = "cmd.winauth.main" global.Log.WithField("func", fn).Info("winauth-go starting") release, alreadyRunning, err := win32.AcquireInstanceLock(singleInstanceMutex) if err != nil { global.Log.WithField("func", fn).WithError(err).Warn("instance lock failed; continuing without it") } if alreadyRunning { global.Log.WithField("func", fn).Info("another instance already running; activating it") if err := win32.ActivateOtherInstance(windowTitle); err != nil { global.Log.WithField("func", fn).WithError(err).Warn("could not activate existing window") } return nil } if release != nil { defer release() } if err := i18n.Init(""); err != nil { global.Log.WithField("func", fn).WithError(err).Warn("i18n init failed; falling back to keys") } return ui.Run(configPath) }, } root.Flags().BoolVar(&console, "console", false, "Show console window (Windows GUI builds)") root.Flags().StringVar(&logLevel, "log-level", "info", "Log level: panic|fatal|error|warn|info|debug|trace (or 0..6)") root.Flags().StringVar(&logFile, "log-file", "", "If set, also write logs to this file in addition to the console") root.Flags().StringVar(&configPath, "config", "", "Path to the YAML config file (default: %APPDATA%\\winauth-go\\config.yaml or $XDG_CONFIG_HOME/winauth-go/config.yaml)") if err := root.Execute(); err != nil { fmt.Fprintln(os.Stderr, "error:", err) os.Exit(1) } }