1package command
2
3import (
4	"os"
5	"os/signal"
6	"syscall"
7
8	"github.com/hashicorp/vault/audit"
9	"github.com/hashicorp/vault/builtin/plugin"
10	"github.com/hashicorp/vault/sdk/logical"
11	"github.com/hashicorp/vault/sdk/physical"
12	"github.com/hashicorp/vault/sdk/version"
13	"github.com/mitchellh/cli"
14
15	/*
16		The builtinplugins package is initialized here because it, in turn,
17		initializes the database plugins.
18		They register multiple database drivers for the "database/sql" package.
19	*/
20	_ "github.com/hashicorp/vault/helper/builtinplugins"
21
22	auditFile "github.com/hashicorp/vault/builtin/audit/file"
23	auditSocket "github.com/hashicorp/vault/builtin/audit/socket"
24	auditSyslog "github.com/hashicorp/vault/builtin/audit/syslog"
25
26	credAliCloud "github.com/hashicorp/vault-plugin-auth-alicloud"
27	credCentrify "github.com/hashicorp/vault-plugin-auth-centrify"
28	credCF "github.com/hashicorp/vault-plugin-auth-cf"
29	credGcp "github.com/hashicorp/vault-plugin-auth-gcp/plugin"
30	credOIDC "github.com/hashicorp/vault-plugin-auth-jwt"
31	credKerb "github.com/hashicorp/vault-plugin-auth-kerberos"
32	credOCI "github.com/hashicorp/vault-plugin-auth-oci"
33	credAws "github.com/hashicorp/vault/builtin/credential/aws"
34	credCert "github.com/hashicorp/vault/builtin/credential/cert"
35	credGitHub "github.com/hashicorp/vault/builtin/credential/github"
36	credLdap "github.com/hashicorp/vault/builtin/credential/ldap"
37	credOkta "github.com/hashicorp/vault/builtin/credential/okta"
38	credToken "github.com/hashicorp/vault/builtin/credential/token"
39	credUserpass "github.com/hashicorp/vault/builtin/credential/userpass"
40
41	logicalKv "github.com/hashicorp/vault-plugin-secrets-kv"
42	logicalDb "github.com/hashicorp/vault/builtin/logical/database"
43
44	physAliCloudOSS "github.com/hashicorp/vault/physical/alicloudoss"
45	physAzure "github.com/hashicorp/vault/physical/azure"
46	physCassandra "github.com/hashicorp/vault/physical/cassandra"
47	physCockroachDB "github.com/hashicorp/vault/physical/cockroachdb"
48	physConsul "github.com/hashicorp/vault/physical/consul"
49	physCouchDB "github.com/hashicorp/vault/physical/couchdb"
50	physDynamoDB "github.com/hashicorp/vault/physical/dynamodb"
51	physEtcd "github.com/hashicorp/vault/physical/etcd"
52	physFoundationDB "github.com/hashicorp/vault/physical/foundationdb"
53	physGCS "github.com/hashicorp/vault/physical/gcs"
54	physManta "github.com/hashicorp/vault/physical/manta"
55	physMSSQL "github.com/hashicorp/vault/physical/mssql"
56	physMySQL "github.com/hashicorp/vault/physical/mysql"
57	physOCI "github.com/hashicorp/vault/physical/oci"
58	physPostgreSQL "github.com/hashicorp/vault/physical/postgresql"
59	physRaft "github.com/hashicorp/vault/physical/raft"
60	physS3 "github.com/hashicorp/vault/physical/s3"
61	physSpanner "github.com/hashicorp/vault/physical/spanner"
62	physSwift "github.com/hashicorp/vault/physical/swift"
63	physZooKeeper "github.com/hashicorp/vault/physical/zookeeper"
64	physFile "github.com/hashicorp/vault/sdk/physical/file"
65	physInmem "github.com/hashicorp/vault/sdk/physical/inmem"
66
67	sr "github.com/hashicorp/vault/serviceregistration"
68	csr "github.com/hashicorp/vault/serviceregistration/consul"
69	ksr "github.com/hashicorp/vault/serviceregistration/kubernetes"
70)
71
72const (
73	// EnvVaultCLINoColor is an env var that toggles colored UI output.
74	EnvVaultCLINoColor = `VAULT_CLI_NO_COLOR`
75	// EnvVaultFormat is the output format
76	EnvVaultFormat = `VAULT_FORMAT`
77
78	// flagNameAddress is the flag used in the base command to read in the
79	// address of the Vault server.
80	flagNameAddress = "address"
81	// flagnameCACert is the flag used in the base command to read in the CA
82	// cert.
83	flagNameCACert = "ca-cert"
84	// flagnameCAPath is the flag used in the base command to read in the CA
85	// cert path.
86	flagNameCAPath = "ca-path"
87	//flagNameClientCert is the flag used in the base command to read in the
88	//client key
89	flagNameClientKey = "client-key"
90	//flagNameClientCert is the flag used in the base command to read in the
91	//client cert
92	flagNameClientCert = "client-cert"
93	// flagNameTLSSkipVerify is the flag used in the base command to read in
94	// the option to ignore TLS certificate verification.
95	flagNameTLSSkipVerify = "tls-skip-verify"
96	// flagTLSServerName is the flag used in the base command to read in
97	// the TLS server name.
98	flagTLSServerName = "tls-server-name"
99	// flagNameAuditNonHMACRequestKeys is the flag name used for auth/secrets enable
100	flagNameAuditNonHMACRequestKeys = "audit-non-hmac-request-keys"
101	// flagNameAuditNonHMACResponseKeys is the flag name used for auth/secrets enable
102	flagNameAuditNonHMACResponseKeys = "audit-non-hmac-response-keys"
103	// flagNameDescription is the flag name used for tuning the secret and auth mount description parameter
104	flagNameDescription = "description"
105	// flagListingVisibility is the flag to toggle whether to show the mount in the UI-specific listing endpoint
106	flagNameListingVisibility = "listing-visibility"
107	// flagNamePassthroughRequestHeaders is the flag name used to set passthrough request headers to the backend
108	flagNamePassthroughRequestHeaders = "passthrough-request-headers"
109	// flagNameAllowedResponseHeaders is used to set allowed response headers from a plugin
110	flagNameAllowedResponseHeaders = "allowed-response-headers"
111	// flagNameTokenType is the flag name used to force a specific token type
112	flagNameTokenType = "token-type"
113)
114
115var (
116	auditBackends = map[string]audit.Factory{
117		"file":   auditFile.Factory,
118		"socket": auditSocket.Factory,
119		"syslog": auditSyslog.Factory,
120	}
121
122	credentialBackends = map[string]logical.Factory{
123		"plugin": plugin.Factory,
124	}
125
126	logicalBackends = map[string]logical.Factory{
127		"plugin":   plugin.Factory,
128		"database": logicalDb.Factory,
129		// This is also available in the plugin catalog, but is here due to the need to
130		// automatically mount it.
131		"kv": logicalKv.Factory,
132	}
133
134	physicalBackends = map[string]physical.Factory{
135		"alicloudoss":            physAliCloudOSS.NewAliCloudOSSBackend,
136		"azure":                  physAzure.NewAzureBackend,
137		"cassandra":              physCassandra.NewCassandraBackend,
138		"cockroachdb":            physCockroachDB.NewCockroachDBBackend,
139		"consul":                 physConsul.NewConsulBackend,
140		"couchdb_transactional":  physCouchDB.NewTransactionalCouchDBBackend,
141		"couchdb":                physCouchDB.NewCouchDBBackend,
142		"dynamodb":               physDynamoDB.NewDynamoDBBackend,
143		"etcd":                   physEtcd.NewEtcdBackend,
144		"file_transactional":     physFile.NewTransactionalFileBackend,
145		"file":                   physFile.NewFileBackend,
146		"foundationdb":           physFoundationDB.NewFDBBackend,
147		"gcs":                    physGCS.NewBackend,
148		"inmem_ha":               physInmem.NewInmemHA,
149		"inmem_transactional_ha": physInmem.NewTransactionalInmemHA,
150		"inmem_transactional":    physInmem.NewTransactionalInmem,
151		"inmem":                  physInmem.NewInmem,
152		"manta":                  physManta.NewMantaBackend,
153		"mssql":                  physMSSQL.NewMSSQLBackend,
154		"mysql":                  physMySQL.NewMySQLBackend,
155		"oci":                    physOCI.NewBackend,
156		"postgresql":             physPostgreSQL.NewPostgreSQLBackend,
157		"s3":                     physS3.NewS3Backend,
158		"spanner":                physSpanner.NewBackend,
159		"swift":                  physSwift.NewSwiftBackend,
160		"raft":                   physRaft.NewRaftBackend,
161		"zookeeper":              physZooKeeper.NewZooKeeperBackend,
162	}
163
164	serviceRegistrations = map[string]sr.Factory{
165		"consul":     csr.NewServiceRegistration,
166		"kubernetes": ksr.NewServiceRegistration,
167	}
168)
169
170// Commands is the mapping of all the available commands.
171var Commands map[string]cli.CommandFactory
172
173func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
174	loginHandlers := map[string]LoginHandler{
175		"alicloud": &credAliCloud.CLIHandler{},
176		"aws":      &credAws.CLIHandler{},
177		"centrify": &credCentrify.CLIHandler{},
178		"cert":     &credCert.CLIHandler{},
179		"cf":       &credCF.CLIHandler{},
180		"gcp":      &credGcp.CLIHandler{},
181		"github":   &credGitHub.CLIHandler{},
182		"kerberos": &credKerb.CLIHandler{},
183		"ldap":     &credLdap.CLIHandler{},
184		"oci":      &credOCI.CLIHandler{},
185		"oidc":     &credOIDC.CLIHandler{},
186		"okta":     &credOkta.CLIHandler{},
187		"pcf":      &credCF.CLIHandler{}, // Deprecated.
188		"radius": &credUserpass.CLIHandler{
189			DefaultMount: "radius",
190		},
191		"token": &credToken.CLIHandler{},
192		"userpass": &credUserpass.CLIHandler{
193			DefaultMount: "userpass",
194		},
195	}
196
197	getBaseCommand := func() *BaseCommand {
198		return &BaseCommand{
199			UI:          ui,
200			tokenHelper: runOpts.TokenHelper,
201			flagAddress: runOpts.Address,
202			client:      runOpts.Client,
203		}
204	}
205
206	Commands = map[string]cli.CommandFactory{
207		"agent": func() (cli.Command, error) {
208			return &AgentCommand{
209				BaseCommand: &BaseCommand{
210					UI: serverCmdUi,
211				},
212				ShutdownCh: MakeShutdownCh(),
213			}, nil
214		},
215		"audit": func() (cli.Command, error) {
216			return &AuditCommand{
217				BaseCommand: getBaseCommand(),
218			}, nil
219		},
220		"audit disable": func() (cli.Command, error) {
221			return &AuditDisableCommand{
222				BaseCommand: getBaseCommand(),
223			}, nil
224		},
225		"audit enable": func() (cli.Command, error) {
226			return &AuditEnableCommand{
227				BaseCommand: getBaseCommand(),
228			}, nil
229		},
230		"audit list": func() (cli.Command, error) {
231			return &AuditListCommand{
232				BaseCommand: getBaseCommand(),
233			}, nil
234		},
235		"auth tune": func() (cli.Command, error) {
236			return &AuthTuneCommand{
237				BaseCommand: getBaseCommand(),
238			}, nil
239		},
240		"auth": func() (cli.Command, error) {
241			return &AuthCommand{
242				BaseCommand: getBaseCommand(),
243			}, nil
244		},
245		"auth disable": func() (cli.Command, error) {
246			return &AuthDisableCommand{
247				BaseCommand: getBaseCommand(),
248			}, nil
249		},
250		"auth enable": func() (cli.Command, error) {
251			return &AuthEnableCommand{
252				BaseCommand: getBaseCommand(),
253			}, nil
254		},
255		"auth help": func() (cli.Command, error) {
256			return &AuthHelpCommand{
257				BaseCommand: getBaseCommand(),
258				Handlers:    loginHandlers,
259			}, nil
260		},
261		"auth list": func() (cli.Command, error) {
262			return &AuthListCommand{
263				BaseCommand: getBaseCommand(),
264			}, nil
265		},
266		"debug": func() (cli.Command, error) {
267			return &DebugCommand{
268				BaseCommand: getBaseCommand(),
269				ShutdownCh:  MakeShutdownCh(),
270			}, nil
271		},
272		"delete": func() (cli.Command, error) {
273			return &DeleteCommand{
274				BaseCommand: getBaseCommand(),
275			}, nil
276		},
277		"lease": func() (cli.Command, error) {
278			return &LeaseCommand{
279				BaseCommand: getBaseCommand(),
280			}, nil
281		},
282		"lease renew": func() (cli.Command, error) {
283			return &LeaseRenewCommand{
284				BaseCommand: getBaseCommand(),
285			}, nil
286		},
287		"lease revoke": func() (cli.Command, error) {
288			return &LeaseRevokeCommand{
289				BaseCommand: getBaseCommand(),
290			}, nil
291		},
292		"list": func() (cli.Command, error) {
293			return &ListCommand{
294				BaseCommand: getBaseCommand(),
295			}, nil
296		},
297		"login": func() (cli.Command, error) {
298			return &LoginCommand{
299				BaseCommand: getBaseCommand(),
300				Handlers:    loginHandlers,
301			}, nil
302		},
303		"namespace": func() (cli.Command, error) {
304			return &NamespaceCommand{
305				BaseCommand: getBaseCommand(),
306			}, nil
307		},
308		"namespace list": func() (cli.Command, error) {
309			return &NamespaceListCommand{
310				BaseCommand: getBaseCommand(),
311			}, nil
312		},
313		"namespace lookup": func() (cli.Command, error) {
314			return &NamespaceLookupCommand{
315				BaseCommand: getBaseCommand(),
316			}, nil
317		},
318		"namespace create": func() (cli.Command, error) {
319			return &NamespaceCreateCommand{
320				BaseCommand: getBaseCommand(),
321			}, nil
322		},
323		"namespace delete": func() (cli.Command, error) {
324			return &NamespaceDeleteCommand{
325				BaseCommand: getBaseCommand(),
326			}, nil
327		},
328		"operator": func() (cli.Command, error) {
329			return &OperatorCommand{
330				BaseCommand: getBaseCommand(),
331			}, nil
332		},
333		"operator generate-root": func() (cli.Command, error) {
334			return &OperatorGenerateRootCommand{
335				BaseCommand: getBaseCommand(),
336			}, nil
337		},
338		"operator init": func() (cli.Command, error) {
339			return &OperatorInitCommand{
340				BaseCommand: getBaseCommand(),
341			}, nil
342		},
343		"operator key-status": func() (cli.Command, error) {
344			return &OperatorKeyStatusCommand{
345				BaseCommand: getBaseCommand(),
346			}, nil
347		},
348		"operator migrate": func() (cli.Command, error) {
349			return &OperatorMigrateCommand{
350				BaseCommand:      getBaseCommand(),
351				PhysicalBackends: physicalBackends,
352				ShutdownCh:       MakeShutdownCh(),
353			}, nil
354		},
355		"operator raft": func() (cli.Command, error) {
356			return &OperatorRaftCommand{
357				BaseCommand: getBaseCommand(),
358			}, nil
359		},
360		"operator raft configuration": func() (cli.Command, error) {
361			return &OperatorRaftConfigurationCommand{
362				BaseCommand: getBaseCommand(),
363			}, nil
364		},
365		"operator raft join": func() (cli.Command, error) {
366			return &OperatorRaftJoinCommand{
367				BaseCommand: getBaseCommand(),
368			}, nil
369		},
370		"operator raft remove-peer": func() (cli.Command, error) {
371			return &OperatorRaftRemovePeerCommand{
372				BaseCommand: getBaseCommand(),
373			}, nil
374		},
375		"operator raft snapshot": func() (cli.Command, error) {
376			return &OperatorRaftSnapshotCommand{
377				BaseCommand: getBaseCommand(),
378			}, nil
379		},
380		"operator raft snapshot restore": func() (cli.Command, error) {
381			return &OperatorRaftSnapshotRestoreCommand{
382				BaseCommand: getBaseCommand(),
383			}, nil
384		},
385		"operator raft snapshot save": func() (cli.Command, error) {
386			return &OperatorRaftSnapshotSaveCommand{
387				BaseCommand: getBaseCommand(),
388			}, nil
389		},
390		"operator rekey": func() (cli.Command, error) {
391			return &OperatorRekeyCommand{
392				BaseCommand: getBaseCommand(),
393			}, nil
394		},
395		"operator rotate": func() (cli.Command, error) {
396			return &OperatorRotateCommand{
397				BaseCommand: getBaseCommand(),
398			}, nil
399		},
400		"operator seal": func() (cli.Command, error) {
401			return &OperatorSealCommand{
402				BaseCommand: getBaseCommand(),
403			}, nil
404		},
405		"operator step-down": func() (cli.Command, error) {
406			return &OperatorStepDownCommand{
407				BaseCommand: getBaseCommand(),
408			}, nil
409		},
410		"operator unseal": func() (cli.Command, error) {
411			return &OperatorUnsealCommand{
412				BaseCommand: getBaseCommand(),
413			}, nil
414		},
415		"path-help": func() (cli.Command, error) {
416			return &PathHelpCommand{
417				BaseCommand: getBaseCommand(),
418			}, nil
419		},
420		"plugin": func() (cli.Command, error) {
421			return &PluginCommand{
422				BaseCommand: getBaseCommand(),
423			}, nil
424		},
425		"plugin deregister": func() (cli.Command, error) {
426			return &PluginDeregisterCommand{
427				BaseCommand: getBaseCommand(),
428			}, nil
429		},
430		"plugin info": func() (cli.Command, error) {
431			return &PluginInfoCommand{
432				BaseCommand: getBaseCommand(),
433			}, nil
434		},
435		"plugin list": func() (cli.Command, error) {
436			return &PluginListCommand{
437				BaseCommand: getBaseCommand(),
438			}, nil
439		},
440		"plugin register": func() (cli.Command, error) {
441			return &PluginRegisterCommand{
442				BaseCommand: getBaseCommand(),
443			}, nil
444		},
445		"policy": func() (cli.Command, error) {
446			return &PolicyCommand{
447				BaseCommand: getBaseCommand(),
448			}, nil
449		},
450		"policy delete": func() (cli.Command, error) {
451			return &PolicyDeleteCommand{
452				BaseCommand: getBaseCommand(),
453			}, nil
454		},
455		"policy fmt": func() (cli.Command, error) {
456			return &PolicyFmtCommand{
457				BaseCommand: getBaseCommand(),
458			}, nil
459		},
460		"policy list": func() (cli.Command, error) {
461			return &PolicyListCommand{
462				BaseCommand: getBaseCommand(),
463			}, nil
464		},
465		"policy read": func() (cli.Command, error) {
466			return &PolicyReadCommand{
467				BaseCommand: getBaseCommand(),
468			}, nil
469		},
470		"policy write": func() (cli.Command, error) {
471			return &PolicyWriteCommand{
472				BaseCommand: getBaseCommand(),
473			}, nil
474		},
475		"print": func() (cli.Command, error) {
476			return &PrintCommand{
477				BaseCommand: getBaseCommand(),
478			}, nil
479		},
480		"print token": func() (cli.Command, error) {
481			return &PrintTokenCommand{
482				BaseCommand: getBaseCommand(),
483			}, nil
484		},
485		"read": func() (cli.Command, error) {
486			return &ReadCommand{
487				BaseCommand: getBaseCommand(),
488			}, nil
489		},
490		"secrets": func() (cli.Command, error) {
491			return &SecretsCommand{
492				BaseCommand: getBaseCommand(),
493			}, nil
494		},
495		"secrets disable": func() (cli.Command, error) {
496			return &SecretsDisableCommand{
497				BaseCommand: getBaseCommand(),
498			}, nil
499		},
500		"secrets enable": func() (cli.Command, error) {
501			return &SecretsEnableCommand{
502				BaseCommand: getBaseCommand(),
503			}, nil
504		},
505		"secrets list": func() (cli.Command, error) {
506			return &SecretsListCommand{
507				BaseCommand: getBaseCommand(),
508			}, nil
509		},
510		"secrets move": func() (cli.Command, error) {
511			return &SecretsMoveCommand{
512				BaseCommand: getBaseCommand(),
513			}, nil
514		},
515		"secrets tune": func() (cli.Command, error) {
516			return &SecretsTuneCommand{
517				BaseCommand: getBaseCommand(),
518			}, nil
519		},
520		"server": func() (cli.Command, error) {
521			return &ServerCommand{
522				BaseCommand: &BaseCommand{
523					UI:          serverCmdUi,
524					tokenHelper: runOpts.TokenHelper,
525					flagAddress: runOpts.Address,
526				},
527				AuditBackends:      auditBackends,
528				CredentialBackends: credentialBackends,
529				LogicalBackends:    logicalBackends,
530				PhysicalBackends:   physicalBackends,
531
532				ServiceRegistrations: serviceRegistrations,
533
534				ShutdownCh: MakeShutdownCh(),
535				SighupCh:   MakeSighupCh(),
536				SigUSR2Ch:  MakeSigUSR2Ch(),
537			}, nil
538		},
539		"ssh": func() (cli.Command, error) {
540			return &SSHCommand{
541				BaseCommand: getBaseCommand(),
542			}, nil
543		},
544		"status": func() (cli.Command, error) {
545			return &StatusCommand{
546				BaseCommand: getBaseCommand(),
547			}, nil
548		},
549		"token": func() (cli.Command, error) {
550			return &TokenCommand{
551				BaseCommand: getBaseCommand(),
552			}, nil
553		},
554		"token create": func() (cli.Command, error) {
555			return &TokenCreateCommand{
556				BaseCommand: getBaseCommand(),
557			}, nil
558		},
559		"token capabilities": func() (cli.Command, error) {
560			return &TokenCapabilitiesCommand{
561				BaseCommand: getBaseCommand(),
562			}, nil
563		},
564		"token lookup": func() (cli.Command, error) {
565			return &TokenLookupCommand{
566				BaseCommand: getBaseCommand(),
567			}, nil
568		},
569		"token renew": func() (cli.Command, error) {
570			return &TokenRenewCommand{
571				BaseCommand: getBaseCommand(),
572			}, nil
573		},
574		"token revoke": func() (cli.Command, error) {
575			return &TokenRevokeCommand{
576				BaseCommand: getBaseCommand(),
577			}, nil
578		},
579		"unwrap": func() (cli.Command, error) {
580			return &UnwrapCommand{
581				BaseCommand: getBaseCommand(),
582			}, nil
583		},
584		"version": func() (cli.Command, error) {
585			return &VersionCommand{
586				VersionInfo: version.GetVersion(),
587				BaseCommand: getBaseCommand(),
588			}, nil
589		},
590		"write": func() (cli.Command, error) {
591			return &WriteCommand{
592				BaseCommand: getBaseCommand(),
593			}, nil
594		},
595		"kv": func() (cli.Command, error) {
596			return &KVCommand{
597				BaseCommand: getBaseCommand(),
598			}, nil
599		},
600		"kv put": func() (cli.Command, error) {
601			return &KVPutCommand{
602				BaseCommand: getBaseCommand(),
603			}, nil
604		},
605		"kv patch": func() (cli.Command, error) {
606			return &KVPatchCommand{
607				BaseCommand: getBaseCommand(),
608			}, nil
609		},
610		"kv rollback": func() (cli.Command, error) {
611			return &KVRollbackCommand{
612				BaseCommand: getBaseCommand(),
613			}, nil
614		},
615		"kv get": func() (cli.Command, error) {
616			return &KVGetCommand{
617				BaseCommand: getBaseCommand(),
618			}, nil
619		},
620		"kv delete": func() (cli.Command, error) {
621			return &KVDeleteCommand{
622				BaseCommand: getBaseCommand(),
623			}, nil
624		},
625		"kv list": func() (cli.Command, error) {
626			return &KVListCommand{
627				BaseCommand: getBaseCommand(),
628			}, nil
629		},
630		"kv destroy": func() (cli.Command, error) {
631			return &KVDestroyCommand{
632				BaseCommand: getBaseCommand(),
633			}, nil
634		},
635		"kv undelete": func() (cli.Command, error) {
636			return &KVUndeleteCommand{
637				BaseCommand: getBaseCommand(),
638			}, nil
639		},
640		"kv enable-versioning": func() (cli.Command, error) {
641			return &KVEnableVersioningCommand{
642				BaseCommand: getBaseCommand(),
643			}, nil
644		},
645		"kv metadata": func() (cli.Command, error) {
646			return &KVMetadataCommand{
647				BaseCommand: getBaseCommand(),
648			}, nil
649		},
650		"kv metadata put": func() (cli.Command, error) {
651			return &KVMetadataPutCommand{
652				BaseCommand: getBaseCommand(),
653			}, nil
654		},
655		"kv metadata get": func() (cli.Command, error) {
656			return &KVMetadataGetCommand{
657				BaseCommand: getBaseCommand(),
658			}, nil
659		},
660		"kv metadata delete": func() (cli.Command, error) {
661			return &KVMetadataDeleteCommand{
662				BaseCommand: getBaseCommand(),
663			}, nil
664		},
665	}
666}
667
668// MakeShutdownCh returns a channel that can be used for shutdown
669// notifications for commands. This channel will send a message for every
670// SIGINT or SIGTERM received.
671func MakeShutdownCh() chan struct{} {
672	resultCh := make(chan struct{})
673
674	shutdownCh := make(chan os.Signal, 4)
675	signal.Notify(shutdownCh, os.Interrupt, syscall.SIGTERM)
676	go func() {
677		<-shutdownCh
678		close(resultCh)
679	}()
680	return resultCh
681}
682
683// MakeSighupCh returns a channel that can be used for SIGHUP
684// reloading. This channel will send a message for every
685// SIGHUP received.
686func MakeSighupCh() chan struct{} {
687	resultCh := make(chan struct{})
688
689	signalCh := make(chan os.Signal, 4)
690	signal.Notify(signalCh, syscall.SIGHUP)
691	go func() {
692		for {
693			<-signalCh
694			resultCh <- struct{}{}
695		}
696	}()
697	return resultCh
698}
699