github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libcmdline/cmdline.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package libcmdline
     5  
     6  import (
     7  	"fmt"
     8  	"io"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/keybase/cli"
    13  	"github.com/keybase/client/go/libkb"
    14  )
    15  
    16  type Command interface {
    17  	libkb.Command
    18  	ParseArgv(*cli.Context) error // A command-specific parse-args
    19  	Run() error                   // Run in client mode
    20  }
    21  
    22  type ForkCmd int
    23  type LogForward int
    24  
    25  const (
    26  	NormalFork ForkCmd = iota
    27  	NoFork
    28  	ForceFork
    29  )
    30  
    31  const (
    32  	LogForwardNormal LogForward = iota
    33  	LogForwardNone
    34  )
    35  
    36  type CommandLine struct {
    37  	app                   *cli.App
    38  	ctx                   *cli.Context
    39  	cmd                   Command
    40  	name                  string     // the name of the chosen command
    41  	service               bool       // The server is a special command
    42  	fork                  ForkCmd    // If the command is to stop (then don't start the server)
    43  	noStandalone          bool       // On if this command can't run in standalone mode
    44  	logForward            LogForward // What do to about log forwarding
    45  	skipOutOfDateCheck    bool       // don't try to check for service being out of date
    46  	skipAccountResetCheck bool       // don't check if our account is scheduled for rest
    47  	defaultCmd            string
    48  }
    49  
    50  func (p CommandLine) IsService() bool             { return p.service }
    51  func (p CommandLine) SkipOutOfDateCheck() bool    { return p.skipOutOfDateCheck }
    52  func (p CommandLine) SkipAccountResetCheck() bool { return p.skipAccountResetCheck }
    53  func (p *CommandLine) SetService()                { p.service = true }
    54  func (p CommandLine) GetForkCmd() ForkCmd         { return p.fork }
    55  func (p *CommandLine) SetForkCmd(v ForkCmd)       { p.fork = v }
    56  func (p *CommandLine) SetNoStandalone()           { p.noStandalone = true }
    57  func (p CommandLine) IsNoStandalone() bool        { return p.noStandalone }
    58  func (p *CommandLine) SetLogForward(f LogForward) { p.logForward = f }
    59  func (p *CommandLine) GetLogForward() LogForward  { return p.logForward }
    60  func (p *CommandLine) SetSkipOutOfDateCheck()     { p.skipOutOfDateCheck = true }
    61  func (p *CommandLine) SetSkipAccountResetCheck()  { p.skipAccountResetCheck = true }
    62  
    63  func (p CommandLine) GetNoAutoFork() (bool, bool) {
    64  	return p.GetBool("no-auto-fork", true)
    65  }
    66  func (p CommandLine) GetAutoFork() (bool, bool) {
    67  	return p.GetBool("auto-fork", true)
    68  }
    69  func (p CommandLine) GetHome() string {
    70  	return p.GetGString("home")
    71  }
    72  func (p CommandLine) GetMobileSharedHome() string {
    73  	return p.GetGString("mobile-shared-home")
    74  }
    75  func (p CommandLine) GetServerURI() (string, error) {
    76  	return p.GetGString("server"), nil
    77  }
    78  func (p CommandLine) GetConfigFilename() string {
    79  	return p.GetGString("config-file")
    80  }
    81  func (p CommandLine) GetGUIConfigFilename() string {
    82  	return p.GetGString("gui-config-file")
    83  }
    84  func (p CommandLine) GetUpdaterConfigFilename() string {
    85  	return p.GetGString("updater-config-file")
    86  }
    87  func (p CommandLine) GetDeviceCloneStateFilename() string {
    88  	return p.GetGString("device-clone-state-file")
    89  }
    90  func (p CommandLine) GetSessionFilename() string {
    91  	return p.GetGString("session-file")
    92  }
    93  func (p CommandLine) GetDbFilename() string {
    94  	return p.GetGString("db")
    95  }
    96  func (p CommandLine) GetChatDbFilename() string {
    97  	return p.GetGString("chat-db")
    98  }
    99  func (p CommandLine) GetPvlKitFilename() string {
   100  	return p.GetGString("pvl-kit")
   101  }
   102  func (p CommandLine) GetParamProofKitFilename() string {
   103  	return p.GetGString("paramproof-kit")
   104  }
   105  func (p CommandLine) GetExternalURLKitFilename() string {
   106  	return p.GetGString("externalurl-kit")
   107  }
   108  func (p CommandLine) GetProveBypass() (bool, bool) {
   109  	return p.GetBool("prove-bypass", true)
   110  }
   111  func (p CommandLine) GetDebug() (bool, bool) {
   112  	// --no-debug suppresses --debug. Note that although we don't define a
   113  	// separate GetNoDebug() accessor, fork_server.go still looks for
   114  	// --no-debug by name, to pass it along to an autoforked daemon.
   115  	if noDebug, _ := p.GetBool("no-debug", true); noDebug {
   116  		return false /* val */, true /* isSet */
   117  	}
   118  	return p.GetBool("debug", true)
   119  }
   120  func (p CommandLine) GetDebugJourneycard() (bool, bool) {
   121  	return p.GetBool("debug-journeycard", true)
   122  }
   123  func (p CommandLine) GetDisplayRawUntrustedOutput() (bool, bool) {
   124  	return p.GetBool("display-raw-untrusted-output", true)
   125  }
   126  func (p CommandLine) GetVDebugSetting() string {
   127  	return p.GetGString("vdebug")
   128  }
   129  func (p CommandLine) GetPGPFingerprint() *libkb.PGPFingerprint {
   130  	return libkb.PGPFingerprintFromHexNoError(p.GetGString("fingerprint"))
   131  }
   132  func (p CommandLine) GetProxy() string {
   133  	return p.GetGString("proxy")
   134  }
   135  func (p CommandLine) GetLogFile() string {
   136  	return p.GetGString("log-file")
   137  }
   138  func (p CommandLine) GetEKLogFile() string {
   139  	return p.GetGString("ek-log-file")
   140  }
   141  func (p CommandLine) GetPerfLogFile() string {
   142  	return p.GetGString("perf-log-file")
   143  }
   144  func (p CommandLine) GetGUILogFile() string {
   145  	return p.GetGString("gui-log-file")
   146  }
   147  func (p CommandLine) GetUseDefaultLogFile() (bool, bool) {
   148  	return p.GetBool("use-default-log-file", true)
   149  }
   150  func (p CommandLine) GetUseRootConfigFile() (bool, bool) {
   151  	return p.GetBool("use-root-config-file", true)
   152  }
   153  func (p CommandLine) GetLogPrefix() string {
   154  	return p.GetGString("log-prefix")
   155  }
   156  
   157  func (p CommandLine) GetLogFormat() string {
   158  	return p.GetGString("log-format")
   159  }
   160  func (p CommandLine) GetGpgHome() string {
   161  	return p.GetGString("gpg-home")
   162  }
   163  func (p CommandLine) GetAPIDump() (bool, bool) {
   164  	return p.GetBool("api-dump-unsafe", true)
   165  }
   166  func (p CommandLine) GetGregorSaveInterval() (time.Duration, bool) {
   167  	ret, err := p.GetGDuration("push-save-interval")
   168  	if err != nil {
   169  		return 0, false
   170  	}
   171  	return ret, true
   172  }
   173  func (p CommandLine) GetGregorDisabled() (bool, bool) {
   174  	return p.GetBool("push-disabled", true)
   175  }
   176  func (p CommandLine) GetSecretStorePrimingDisabled() (bool, bool) {
   177  	// SecretStorePrimingDisabled is only for tests
   178  	return false, false
   179  }
   180  func (p CommandLine) GetBGIdentifierDisabled() (bool, bool) {
   181  	return p.GetBool("bg-identifier-disabled", true)
   182  }
   183  
   184  func (p CommandLine) GetGregorURI() string {
   185  	return p.GetGString("push-server-uri")
   186  }
   187  func (p CommandLine) GetGregorPingInterval() (time.Duration, bool) {
   188  	ret, err := p.GetGDuration("push-ping-interval")
   189  	if err != nil {
   190  		return 0, false
   191  	}
   192  	return ret, true
   193  }
   194  func (p CommandLine) GetGregorPingTimeout() (time.Duration, bool) {
   195  	ret, err := p.GetGDuration("push-ping-timeout")
   196  	if err != nil {
   197  		return 0, false
   198  	}
   199  	return ret, true
   200  }
   201  
   202  func (p CommandLine) GetChatDelivererInterval() (time.Duration, bool) {
   203  	ret, err := p.GetGDuration("chat-deliverer-interval")
   204  	if err != nil {
   205  		return 0, false
   206  	}
   207  	return ret, true
   208  }
   209  
   210  func (p CommandLine) GetRunMode() (libkb.RunMode, error) {
   211  	return libkb.StringToRunMode(p.GetGString("run-mode"))
   212  }
   213  func (p CommandLine) GetFeatureFlags() (libkb.FeatureFlags, error) {
   214  	return libkb.StringToFeatureFlags(p.GetGString("features")), nil
   215  }
   216  func (p CommandLine) GetPinentry() string {
   217  	return p.GetGString("pinentry")
   218  }
   219  func (p CommandLine) GetAppType() libkb.AppType {
   220  	return libkb.DesktopAppType
   221  }
   222  func (p CommandLine) IsMobileExtension() (bool, bool) {
   223  	return false, false
   224  }
   225  func (p CommandLine) GetSlowGregorConn() (bool, bool) {
   226  	return p.GetBool("slow-gregor-conn", true)
   227  }
   228  func (p CommandLine) GetReadDeletedSigChain() (bool, bool) {
   229  	return p.GetBool("read-deleted-sigchain", true)
   230  }
   231  func (p CommandLine) GetGString(s string) string {
   232  	return p.ctx.GlobalString(s)
   233  }
   234  func (p CommandLine) GetString(s string) string {
   235  	return p.ctx.String(s)
   236  }
   237  func (p CommandLine) GetGInt(s string) int {
   238  	return p.ctx.GlobalInt(s)
   239  }
   240  func (p CommandLine) GetGDuration(s string) (time.Duration, error) {
   241  	return time.ParseDuration(p.GetGString(s))
   242  }
   243  func (p CommandLine) GetGpg() string {
   244  	return p.GetGString("gpg")
   245  }
   246  func (p CommandLine) GetSecretKeyringTemplate() string {
   247  	return p.GetGString("secret-keyring")
   248  }
   249  func (p CommandLine) GetSocketFile() string {
   250  	return p.GetGString("socket-file")
   251  }
   252  func (p CommandLine) GetPidFile() string {
   253  	return p.GetGString("pid-file")
   254  }
   255  func (p CommandLine) GetScraperTimeout() (time.Duration, bool) {
   256  	ret, err := p.GetGDuration("scraper-timeout")
   257  	if err != nil {
   258  		return 0, false
   259  	}
   260  	return ret, true
   261  }
   262  func (p CommandLine) GetAPITimeout() (time.Duration, bool) {
   263  	ret, err := p.GetGDuration("api-timeout")
   264  	if err != nil {
   265  		return 0, false
   266  	}
   267  	return ret, true
   268  }
   269  func (p CommandLine) GetGpgOptions() []string {
   270  	var ret []string
   271  	s := p.GetGString("gpg-options")
   272  	if len(s) > 0 {
   273  		ret = strings.Fields(s)
   274  	}
   275  	return ret
   276  }
   277  
   278  func (p CommandLine) getKIDs(name string) []string {
   279  	s := p.GetGString(name)
   280  	if len(s) == 0 {
   281  		return nil
   282  	}
   283  	return strings.Split(s, ":")
   284  }
   285  
   286  func (p CommandLine) GetMerkleKIDs() []string {
   287  	return p.getKIDs("merkle-kids")
   288  }
   289  
   290  func (p CommandLine) GetCodeSigningKIDs() []string {
   291  	return p.getKIDs("code-signing-kids")
   292  }
   293  
   294  func (p CommandLine) GetUserCacheMaxAge() (time.Duration, bool) {
   295  	ret, err := p.GetGDuration("user-cache-maxage")
   296  	if err != nil {
   297  		return 0, false
   298  	}
   299  	return ret, true
   300  }
   301  
   302  func (p CommandLine) GetProofCacheSize() (int, bool) {
   303  	ret := p.GetGInt("proof-cache-size")
   304  	if ret != 0 {
   305  		return ret, true
   306  	}
   307  	return 0, false
   308  }
   309  
   310  func (p CommandLine) GetLevelDBNumFiles() (int, bool) {
   311  	ret := p.GetGInt("leveldb-num-files")
   312  	if ret != 0 {
   313  		return ret, true
   314  	}
   315  	return 0, false
   316  }
   317  
   318  func (p CommandLine) GetLevelDBWriteBufferMB() (int, bool) {
   319  	ret := p.GetGInt("leveldb-write-buffer-mb")
   320  	if ret != 0 {
   321  		return ret, true
   322  	}
   323  	return 0, false
   324  }
   325  
   326  func (p CommandLine) GetChatInboxSourceLocalizeThreads() (int, bool) {
   327  	ret := p.GetGInt("chat-inboxsource-localizethreads")
   328  	if ret != 0 {
   329  		return ret, true
   330  	}
   331  	return 0, false
   332  }
   333  
   334  func (p CommandLine) GetLinkCacheSize() (int, bool) {
   335  	ret := p.GetGInt("link-cache-size")
   336  	if ret != 0 {
   337  		return ret, true
   338  	}
   339  	return 0, false
   340  }
   341  
   342  func (p CommandLine) GetUPAKCacheSize() (int, bool) {
   343  	ret := p.GetGInt("upak-cache-size")
   344  	if ret != 0 {
   345  		return ret, true
   346  	}
   347  	return 0, false
   348  }
   349  
   350  func (p CommandLine) GetUIDMapFullNameCacheSize() (int, bool) {
   351  	ret := p.GetGInt("uid-map-full-name-cache-size")
   352  	if ret != 0 {
   353  		return ret, true
   354  	}
   355  	return 0, false
   356  }
   357  
   358  func (p CommandLine) GetPayloadCacheSize() (int, bool) {
   359  	ret := p.GetGInt("payload-cache-size")
   360  	if ret != 0 {
   361  		return ret, true
   362  	}
   363  	return 0, false
   364  }
   365  
   366  func (p CommandLine) GetLocalTrackMaxAge() (time.Duration, bool) {
   367  	ret, err := p.GetGDuration("local-track-maxage")
   368  	if err != nil {
   369  		return 0, false
   370  	}
   371  	return ret, true
   372  }
   373  
   374  func (p CommandLine) GetStandalone() (bool, bool) {
   375  	return p.GetBool("standalone", true)
   376  }
   377  
   378  func (p CommandLine) GetLocalRPCDebug() string {
   379  	return p.GetGString("local-rpc-debug-unsafe")
   380  }
   381  
   382  func (p CommandLine) GetTimers() string {
   383  	return p.GetGString("timers")
   384  }
   385  
   386  func (p CommandLine) GetTorMode() (ret libkb.TorMode, err error) {
   387  	if s := p.GetGString("tor-mode"); s != "" {
   388  		ret, err = libkb.StringToTorMode(s)
   389  	}
   390  	return ret, err
   391  }
   392  
   393  func (p CommandLine) GetTorHiddenAddress() string {
   394  	return p.GetGString("tor-hidden-address")
   395  }
   396  func (p CommandLine) GetTorProxy() string {
   397  	return p.GetGString("tor-proxy")
   398  }
   399  
   400  func (p CommandLine) GetProxyType() string {
   401  	return p.GetGString("proxy-type")
   402  }
   403  
   404  func (p CommandLine) IsCertPinningEnabled() bool {
   405  	r1, _ := p.GetBool("disable-cert-pinning", true)
   406  	// Defaults to false since it is a boolean flag, so just invert it
   407  	return !r1
   408  }
   409  
   410  func (p CommandLine) GetMountDir() string {
   411  	return p.GetGString("mountdir")
   412  }
   413  
   414  func (p CommandLine) GetMountDirDefault() string {
   415  	return p.GetGString("mountdirdefault")
   416  }
   417  
   418  func (p CommandLine) GetRememberPassphrase(libkb.NormalizedUsername) (bool, bool) {
   419  	return p.GetBool("remember-passphrase", true)
   420  }
   421  
   422  func (p CommandLine) GetAttachmentDisableMulti() (bool, bool) {
   423  	return p.GetBool("attachment-disable-multi", true)
   424  }
   425  
   426  func (p CommandLine) GetDisableTeamAuditor() (bool, bool) {
   427  	return p.GetBool("disable-team-auditor", true)
   428  }
   429  
   430  func (p CommandLine) GetDisableTeamBoxAuditor() (bool, bool) {
   431  	return p.GetBool("disable-team-box-auditor", true)
   432  }
   433  
   434  func (p CommandLine) GetDisableEKBackgroundKeygen() (bool, bool) {
   435  	return p.GetBool("disable-ek-backgorund-keygen", true)
   436  }
   437  
   438  func (p CommandLine) GetDisableMerkleAuditor() (bool, bool) {
   439  	return p.GetBool("disable-merkle-auditor", true)
   440  }
   441  
   442  func (p CommandLine) GetDisableSearchIndexer() (bool, bool) {
   443  	return p.GetBool("disable-search-indexer", true)
   444  }
   445  
   446  func (p CommandLine) GetDisableBgConvLoader() (bool, bool) {
   447  	return p.GetBool("disable-bg-conv-loader", true)
   448  }
   449  
   450  func (p CommandLine) GetEnableBotLiteMode() (bool, bool) {
   451  	return p.GetBool("enable-bot-lite-mode", true)
   452  }
   453  
   454  func (p CommandLine) GetExtraNetLogging() (bool, bool) {
   455  	return p.GetBool("extra-net-logging", true)
   456  }
   457  
   458  func (p CommandLine) GetForceLinuxKeyring() (bool, bool) {
   459  	return p.GetBool("force-linux-keyring", true)
   460  }
   461  
   462  func (p CommandLine) GetForceSecretStoreFile() (bool, bool) {
   463  	return false, false // not configurable via command line flags
   464  }
   465  
   466  func (p CommandLine) GetRuntimeStatsEnabled() (bool, bool) {
   467  	return false, false
   468  }
   469  
   470  func (p CommandLine) GetAttachmentHTTPStartPort() (int, bool) {
   471  	ret := p.GetGInt("attachment-httpsrv-port")
   472  	if ret != 0 {
   473  		return ret, true
   474  	}
   475  	return 0, false
   476  }
   477  
   478  func (p CommandLine) GetBool(s string, glbl bool) (bool, bool) {
   479  	var v bool
   480  	if glbl {
   481  		v = p.ctx.GlobalBool(s)
   482  	} else {
   483  		v = p.ctx.Bool(s)
   484  	}
   485  	return v, v
   486  }
   487  
   488  type CmdBaseHelp struct {
   489  	ctx *cli.Context
   490  }
   491  
   492  func (c *CmdBaseHelp) GetUsage() libkb.Usage {
   493  	return libkb.Usage{}
   494  }
   495  func (c *CmdBaseHelp) ParseArgv(*cli.Context) error { return nil }
   496  
   497  type CmdGeneralHelp struct {
   498  	CmdBaseHelp
   499  }
   500  
   501  func (c *CmdBaseHelp) RunClient() error { return c.Run() }
   502  
   503  func (c *CmdBaseHelp) Run() error {
   504  	cli.ShowAppHelp(c.ctx)
   505  	return nil
   506  }
   507  
   508  type CmdSpecificHelp struct {
   509  	CmdBaseHelp
   510  	name string
   511  }
   512  
   513  func (c CmdSpecificHelp) Run() error {
   514  	cli.ShowCommandHelp(c.ctx, c.name)
   515  	return nil
   516  }
   517  
   518  func NewCommandLine(addHelp bool, extraFlags []cli.Flag) *CommandLine {
   519  	app := cli.NewApp()
   520  	ret := &CommandLine{app: app, fork: NormalFork}
   521  	ret.PopulateApp(addHelp, extraFlags)
   522  	return ret
   523  }
   524  
   525  func (p *CommandLine) PopulateApp(addHelp bool, extraFlags []cli.Flag) {
   526  	app := p.app
   527  	app.Name = "keybase"
   528  	app.EnableBashCompletion = true
   529  	app.Version = libkb.VersionString()
   530  	app.Usage = "Keybase command line client."
   531  
   532  	app.Flags = []cli.Flag{
   533  		cli.BoolFlag{
   534  			Name:  "api-dump-unsafe",
   535  			Usage: "Dump API call internals (may leak secrets).",
   536  		},
   537  		cli.StringFlag{
   538  			Name:  "api-timeout",
   539  			Usage: "set the HTTP timeout for API calls to the keybase API server",
   540  		},
   541  		cli.StringFlag{
   542  			Name:  "api-uri-path-prefix",
   543  			Usage: "Specify an alternate API URI path prefix.",
   544  		},
   545  		cli.StringFlag{
   546  			Name:  "app-start-mode",
   547  			Usage: "Specify 'service' to auto-start UI app, or anything else to disable",
   548  		},
   549  		cli.BoolFlag{
   550  			Name:  "bg-identifier-disabled",
   551  			Usage: "supply to disable the BG identifier loop",
   552  		},
   553  		cli.StringFlag{
   554  			Name:  "chat-db",
   555  			Usage: "Specify an alternate local Chat DB location.",
   556  		},
   557  		cli.StringFlag{
   558  			Name:  "code-signing-kids",
   559  			Usage: "Set of code signing key IDs (colon-separated).",
   560  		},
   561  		cli.StringFlag{
   562  			Name:  "config-file, c",
   563  			Usage: "Specify an (alternate) master config file.",
   564  		},
   565  		cli.BoolFlag{
   566  			Name:  "use-root-config-file",
   567  			Usage: "Use the default root config on Linux only.",
   568  		},
   569  		cli.StringFlag{
   570  			Name:  "db",
   571  			Usage: "Specify an alternate local DB location.",
   572  		},
   573  		cli.BoolFlag{
   574  			Name:  "debug, d",
   575  			Usage: "Enable debugging mode.",
   576  		},
   577  		cli.BoolFlag{
   578  			Name: "disable-cert-pinning",
   579  			Usage: "Disable certificate pinning within the app. WARNING: This reduces the security of the app. Do not use " +
   580  				"unless necessary! This should only be used if you are running keybase behind a proxy that does TLS interception.",
   581  		},
   582  		cli.BoolFlag{
   583  			Name:  "display-raw-untrusted-output",
   584  			Usage: "Display output from users (messages, chats, ...) in the terminal without escaping terminal codes. WARNING: maliciously crafted unescaped outputs can overwrite anything you see on the terminal.",
   585  		},
   586  		cli.StringFlag{
   587  			Name:  "features",
   588  			Usage: "specify experimental feature flags",
   589  		},
   590  		cli.StringFlag{
   591  			Name:  "gpg",
   592  			Usage: "Path to GPG client (optional for exporting keys).",
   593  		},
   594  		cli.StringFlag{
   595  			Name:  "gpg-options",
   596  			Usage: "Options to use when calling GPG.",
   597  		},
   598  		cli.StringFlag{
   599  			Name:  "home, H",
   600  			Usage: "Specify an (alternate) home directory.",
   601  		},
   602  		cli.IntFlag{
   603  			Name:  "leveldb-num-files",
   604  			Usage: "Specify the max number of files LevelDB may open",
   605  		},
   606  		cli.StringFlag{
   607  			Name:  "local-rpc-debug-unsafe",
   608  			Usage: "Use to debug local RPC (may leak secrets).",
   609  		},
   610  		cli.StringFlag{
   611  			Name:  "log-file",
   612  			Usage: "Specify a log file for the keybase service.",
   613  		},
   614  		cli.StringFlag{
   615  			Name:  "ek-log-file",
   616  			Usage: "Specify a log file for the keybase ephemeral key log.",
   617  		},
   618  		cli.StringFlag{
   619  			Name:  "log-format",
   620  			Usage: "Log format (default, plain, file, fancy).",
   621  		},
   622  		cli.StringFlag{
   623  			Name:  "log-prefix",
   624  			Usage: "Specify a prefix for a unique log file name.",
   625  		},
   626  		cli.StringFlag{
   627  			Name:  "merkle-kids",
   628  			Usage: "Set of admissible Merkle Tree fingerprints (colon-separated).",
   629  		},
   630  		cli.BoolFlag{
   631  			Name:  "no-debug",
   632  			Usage: "Suppress debugging mode; takes precedence over --debug.",
   633  		},
   634  		cli.StringFlag{
   635  			Name:  "pgpdir, gpgdir",
   636  			Usage: "Specify a PGP directory (default is ~/.gnupg).",
   637  		},
   638  		cli.StringFlag{
   639  			Name:  "pid-file",
   640  			Usage: "Location of the keybased pid-file (to ensure only one running daemon).",
   641  		},
   642  		cli.StringFlag{
   643  			Name:  "pinentry",
   644  			Usage: "Specify a path to find a pinentry program.",
   645  		},
   646  		cli.IntFlag{
   647  			Name:  "proof-cache-size",
   648  			Usage: "Number of proof entries to cache.",
   649  		},
   650  		cli.StringFlag{
   651  			Name:  "proxy",
   652  			Usage: "Specify a proxy to ship all Web requests over; Must be used with --proxy-type; Example: localhost:8080",
   653  		},
   654  		cli.StringFlag{
   655  			Name:  "proxy-type",
   656  			Usage: fmt.Sprintf("set the proxy type; One of: %s", libkb.GetCommaSeparatedListOfProxyTypes()),
   657  		},
   658  		cli.BoolFlag{
   659  			Name:  "push-disabled",
   660  			Usage: "Disable push server connection (which is on by default)",
   661  		},
   662  		cli.IntFlag{
   663  			Name:  "push-save-interval",
   664  			Usage: "Set the interval between saves of the push cache (in seconds)",
   665  		},
   666  		cli.StringFlag{
   667  			Name:  "push-server-uri",
   668  			Usage: "Specify a URI for contacting the Keybase push server",
   669  		},
   670  		cli.StringFlag{
   671  			Name:  "pvl-kit",
   672  			Usage: "Specify an alternate local PVL kit file location.",
   673  		},
   674  		cli.StringFlag{
   675  			Name:  "paramproof-kit",
   676  			Usage: "Specify an alternate local parameterized proof kit file location.",
   677  		},
   678  		cli.BoolFlag{
   679  			Name:  "prove-bypass",
   680  			Usage: "Prove even disabled proof services",
   681  		},
   682  		cli.BoolFlag{
   683  			Name:  "remember-passphrase",
   684  			Usage: "Remember keybase passphrase",
   685  		},
   686  		cli.StringFlag{
   687  			Name:  "run-mode",
   688  			Usage: "Run mode (devel, staging, prod).", // These are defined in libkb/constants.go
   689  		},
   690  		cli.StringFlag{
   691  			Name:  "scraper-timeout",
   692  			Usage: "set the HTTP timeout for external proof scrapers",
   693  		},
   694  		cli.StringFlag{
   695  			Name:  "secret-keyring",
   696  			Usage: "Location of the Keybase secret-keyring (P3SKB-encoded).",
   697  		},
   698  		cli.StringFlag{
   699  			Name:  "server, s",
   700  			Usage: "Specify server API.",
   701  		},
   702  		cli.StringFlag{
   703  			Name:  "session-file",
   704  			Usage: "Specify an alternate session data file.",
   705  		},
   706  		cli.BoolFlag{
   707  			Name:  "slow-gregor-conn",
   708  			Usage: "Slow responses from gregor for testing",
   709  		},
   710  		cli.BoolFlag{
   711  			Name:  "read-deleted-sigchain",
   712  			Usage: "Allow admins to read deleted sigchains for debugging.",
   713  		},
   714  		cli.StringFlag{
   715  			Name:  "socket-file",
   716  			Usage: "Location of the keybased socket-file.",
   717  		},
   718  		cli.BoolFlag{
   719  			Name:  "standalone",
   720  			Usage: "Use the client without any daemon support.",
   721  		},
   722  		cli.StringFlag{
   723  			Name:  "timers",
   724  			Usage: "Specify 'a' for API; 'r' for RPCs; and 'x' for eXternal API calls",
   725  		},
   726  		cli.StringFlag{
   727  			Name:  "tor-hidden-address",
   728  			Usage: fmt.Sprintf("set TOR address of keybase server; defaults to %s", libkb.TorServerURI),
   729  		},
   730  		cli.StringFlag{
   731  			Name:  "tor-mode",
   732  			Usage: "set TOR mode to be 'leaky', 'none', or 'strict'. 'none' by default. See 'help tor' for more details.",
   733  		},
   734  		cli.StringFlag{
   735  			Name:  "tor-proxy",
   736  			Usage: fmt.Sprintf("set TOR proxy; when Tor mode is on; defaults to %s when TOR is enabled", libkb.TorProxy),
   737  		},
   738  		cli.StringFlag{
   739  			Name:  "updater-config-file",
   740  			Usage: "Specify a path to the updater config file",
   741  		},
   742  		cli.StringFlag{
   743  			Name:  "gui-config-file",
   744  			Usage: "Specify a path to the GUI config file",
   745  		},
   746  		cli.BoolFlag{
   747  			Name:  "use-default-log-file",
   748  			Usage: "Log to the default log file in $XDG_CACHE_HOME, or ~/.cache if unset.",
   749  		},
   750  		cli.IntFlag{
   751  			Name:  "user-cache-size",
   752  			Usage: "Number of User entries to cache.",
   753  		},
   754  		cli.StringFlag{
   755  			Name:  "vdebug",
   756  			Usage: "Verbose debugging; takes a comma-joined list of levels and tags",
   757  		},
   758  		cli.BoolFlag{
   759  			Name:  "debug-journeycard",
   760  			Usage: "Enable experimental journey cards",
   761  		},
   762  		cli.BoolFlag{
   763  			Name:  "disable-team-auditor",
   764  			Usage: "Disable auditing of teams",
   765  		},
   766  		cli.BoolFlag{
   767  			Name:  "disable-team-box-auditor",
   768  			Usage: "Disable box auditing of teams",
   769  		},
   770  		cli.BoolFlag{
   771  			Name:  "disable-merkle-auditor",
   772  			Usage: "Disable background probabilistic merkle audit",
   773  		},
   774  		cli.BoolFlag{
   775  			Name:  "disable-search-indexer",
   776  			Usage: "Disable chat search background indexer",
   777  		},
   778  		cli.BoolFlag{
   779  			Name:  "disable-bg-conv-loader",
   780  			Usage: "Disable background conversation loading",
   781  		},
   782  		cli.BoolFlag{
   783  			Name:  "enable-bot-lite-mode",
   784  			Usage: "Enable bot lite mode. Disables non-critical background services for bot performance.",
   785  		},
   786  		cli.BoolFlag{
   787  			Name:  "force-linux-keyring",
   788  			Usage: "Require the use of the OS keyring (Gnome Keyring or KWallet) and fail if not available rather than falling back to file-based secret store.",
   789  		},
   790  		cli.BoolFlag{
   791  			Name:  "extra-net-logging",
   792  			Usage: "Do additional debug logging during network requests.",
   793  		},
   794  	}
   795  	if extraFlags != nil {
   796  		app.Flags = append(app.Flags, extraFlags...)
   797  	}
   798  
   799  	app.Commands = []cli.Command{}
   800  }
   801  
   802  func filter(cmds []cli.Command, fn func(cli.Command) bool) []cli.Command {
   803  	var filter []cli.Command
   804  	for _, cmd := range cmds {
   805  		if fn(cmd) {
   806  			filter = append(filter, cmd)
   807  		}
   808  	}
   809  	return filter
   810  }
   811  
   812  func (p *CommandLine) AddCommands(cmds []cli.Command) {
   813  	cmds = filter(cmds, func(c cli.Command) bool {
   814  		return c.Name != ""
   815  	})
   816  	p.app.Commands = append(p.app.Commands, cmds...)
   817  }
   818  
   819  func (p *CommandLine) SetDefaultCommand(name string, cmd Command) {
   820  	p.defaultCmd = name
   821  	p.app.Action = func(c *cli.Context) {
   822  		p.cmd = cmd
   823  		p.ctx = c
   824  		p.name = name
   825  	}
   826  }
   827  
   828  // Called back from inside our subcommands, when they're picked...
   829  func (p *CommandLine) ChooseCommand(cmd Command, name string, ctx *cli.Context) {
   830  	p.cmd = cmd
   831  	p.name = name
   832  	p.ctx = ctx
   833  }
   834  
   835  func (p *CommandLine) Parse(args []string) (cmd Command, err error) {
   836  	// This is suboptimal, but the default help action when there are
   837  	// no args crashes.
   838  	// (cli sets HelpPrinter to nil when p.app.Run(...) returns.)
   839  	if len(args) == 1 && p.defaultCmd == "help" {
   840  		args = append(args, p.defaultCmd)
   841  	}
   842  
   843  	// Actually pick a command
   844  	err = p.app.Run(args)
   845  
   846  	// Should not be populated
   847  	cmd = p.cmd
   848  
   849  	if err != nil || cmd == nil {
   850  		return
   851  	}
   852  
   853  	// cli.HelpPrinter is nil here...anything that needs it will panic.
   854  
   855  	// If we failed to parse arguments properly, switch to the help command
   856  	if err = p.cmd.ParseArgv(p.ctx); err == nil {
   857  		_, err = p.GetRunMode()
   858  	}
   859  	if err != nil {
   860  		cmd = &CmdSpecificHelp{CmdBaseHelp{p.ctx}, p.name}
   861  	}
   862  
   863  	return
   864  }
   865  
   866  func (p *CommandLine) SetOutputWriter(w io.Writer) {
   867  	p.app.Writer = w
   868  }
   869  
   870  // AddHelpTopics appends topics to the list of help topics for
   871  // this app.
   872  func (p *CommandLine) AddHelpTopics(topics []cli.HelpTopic) {
   873  	p.app.HelpTopics = append(p.app.HelpTopics, topics...)
   874  }