You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
6.5 KiB

  1. // Copyright (c) 2015-2021 MinIO, Inc.
  2. //
  3. // This file is part of MinIO Object Storage stack
  4. //
  5. // This program is free software: you can redistribute it and/or modify
  6. // it under the terms of the GNU Affero General Public License as published by
  7. // the Free Software Foundation, either version 3 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU Affero General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU Affero General Public License
  16. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. package cmd
  18. import (
  19. "fmt"
  20. "io"
  21. "os"
  22. "path/filepath"
  23. "runtime"
  24. "runtime/debug"
  25. "sort"
  26. "strconv"
  27. "strings"
  28. "time"
  29. "github.com/minio/cli"
  30. "github.com/minio/minio/internal/color"
  31. "github.com/minio/minio/internal/logger"
  32. "github.com/minio/pkg/v3/console"
  33. "github.com/minio/pkg/v3/env"
  34. "github.com/minio/pkg/v3/trie"
  35. "github.com/minio/pkg/v3/words"
  36. )
  37. // GlobalFlags - global flags for minio.
  38. var GlobalFlags = []cli.Flag{
  39. // Deprecated flag, so its hidden now - existing deployments will keep working.
  40. cli.StringFlag{
  41. Name: "config-dir, C",
  42. Value: defaultConfigDir.Get(),
  43. Usage: "[DEPRECATED] path to legacy configuration directory",
  44. Hidden: true,
  45. },
  46. cli.StringFlag{
  47. Name: "certs-dir, S",
  48. Value: defaultCertsDir.Get(),
  49. Usage: "path to certs directory",
  50. },
  51. cli.BoolFlag{
  52. Name: "quiet",
  53. Usage: "disable startup and info messages",
  54. },
  55. cli.BoolFlag{
  56. Name: "anonymous",
  57. Usage: "hide sensitive information from logging",
  58. },
  59. cli.BoolFlag{
  60. Name: "json",
  61. Usage: "output logs in JSON format",
  62. },
  63. // Deprecated flag, so its hidden now, existing deployments will keep working.
  64. cli.BoolFlag{
  65. Name: "compat",
  66. Usage: "enable strict S3 compatibility by turning off certain performance optimizations",
  67. Hidden: true,
  68. },
  69. // This flag is hidden and to be used only during certain performance testing.
  70. cli.BoolFlag{
  71. Name: "no-compat",
  72. Usage: "disable strict S3 compatibility by turning on certain performance optimizations",
  73. Hidden: true,
  74. },
  75. }
  76. // Help template for minio.
  77. var minioHelpTemplate = `NAME:
  78. {{.Name}} - {{.Usage}}
  79. DESCRIPTION:
  80. {{.Description}}
  81. USAGE:
  82. {{.HelpName}} {{if .VisibleFlags}}[FLAGS] {{end}}COMMAND{{if .VisibleFlags}}{{end}} [ARGS...]
  83. COMMANDS:
  84. {{range .VisibleCommands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
  85. {{end}}{{if .VisibleFlags}}
  86. FLAGS:
  87. {{range .VisibleFlags}}{{.}}
  88. {{end}}{{end}}
  89. VERSION:
  90. {{.Version}}
  91. `
  92. func newApp(name string) *cli.App {
  93. // Collection of minio commands currently supported are.
  94. commands := []cli.Command{}
  95. // Collection of minio commands currently supported in a trie tree.
  96. commandsTree := trie.NewTrie()
  97. // registerCommand registers a cli command.
  98. registerCommand := func(command cli.Command) {
  99. // avoid registering commands which are not being built (via
  100. // go:build tags)
  101. if command.Name == "" {
  102. return
  103. }
  104. commands = append(commands, command)
  105. commandsTree.Insert(command.Name)
  106. }
  107. findClosestCommands := func(command string) []string {
  108. var closestCommands []string
  109. closestCommands = append(closestCommands, commandsTree.PrefixMatch(command)...)
  110. sort.Strings(closestCommands)
  111. // Suggest other close commands - allow missed, wrongly added and
  112. // even transposed characters
  113. for _, value := range commandsTree.Walk(commandsTree.Root()) {
  114. if sort.SearchStrings(closestCommands, value) < len(closestCommands) {
  115. continue
  116. }
  117. // 2 is arbitrary and represents the max
  118. // allowed number of typed errors
  119. if words.DamerauLevenshteinDistance(command, value) < 2 {
  120. closestCommands = append(closestCommands, value)
  121. }
  122. }
  123. return closestCommands
  124. }
  125. // Register all commands.
  126. registerCommand(serverCmd)
  127. registerCommand(fmtGenCmd)
  128. // Set up app.
  129. cli.HelpFlag = cli.BoolFlag{
  130. Name: "help, h",
  131. Usage: "show help",
  132. }
  133. cli.VersionPrinter = printMinIOVersion
  134. app := cli.NewApp()
  135. app.Name = name
  136. app.Author = "MinIO, Inc."
  137. app.Version = ReleaseTag
  138. app.Usage = "High Performance Object Storage"
  139. app.Description = `Build high performance data infrastructure for machine learning, analytics and application data workloads with MinIO`
  140. app.Flags = GlobalFlags
  141. app.HideHelpCommand = true // Hide `help, h` command, we already have `minio --help`.
  142. app.Commands = commands
  143. app.CustomAppHelpTemplate = minioHelpTemplate
  144. app.CommandNotFound = func(ctx *cli.Context, command string) {
  145. console.Printf("‘%s’ is not a minio sub-command. See ‘minio --help’.\n", command)
  146. closestCommands := findClosestCommands(command)
  147. if len(closestCommands) > 0 {
  148. console.Println()
  149. console.Println("Did you mean one of these?")
  150. for _, cmd := range closestCommands {
  151. console.Printf("\t‘%s’\n", cmd)
  152. }
  153. }
  154. os.Exit(1)
  155. }
  156. return app
  157. }
  158. func startupBanner(banner io.Writer) {
  159. CopyrightYear = strconv.Itoa(time.Now().Year())
  160. fmt.Fprintln(banner, color.Blue("Copyright:")+color.Bold(" 2015-%s MinIO, Inc.", CopyrightYear))
  161. fmt.Fprintln(banner, color.Blue("License:")+color.Bold(" "+MinioLicense))
  162. fmt.Fprintln(banner, color.Blue("Version:")+color.Bold(" %s (%s %s/%s)", ReleaseTag, runtime.Version(), runtime.GOOS, runtime.GOARCH))
  163. }
  164. func versionBanner(c *cli.Context) io.Reader {
  165. banner := &strings.Builder{}
  166. fmt.Fprintln(banner, color.Bold("%s version %s (commit-id=%s)", c.App.Name, c.App.Version, CommitID))
  167. fmt.Fprintln(banner, color.Blue("Runtime:")+color.Bold(" %s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH))
  168. fmt.Fprintln(banner, color.Blue("License:")+color.Bold(" GNU AGPLv3 - https://www.gnu.org/licenses/agpl-3.0.html"))
  169. fmt.Fprintln(banner, color.Blue("Copyright:")+color.Bold(" 2015-%s MinIO, Inc.", CopyrightYear))
  170. return strings.NewReader(banner.String())
  171. }
  172. func printMinIOVersion(c *cli.Context) {
  173. io.Copy(c.App.Writer, versionBanner(c))
  174. }
  175. var debugNoExit = env.Get("_MINIO_DEBUG_NO_EXIT", "") != ""
  176. // Main main for minio server.
  177. func Main(args []string) {
  178. // Set the minio app name.
  179. appName := filepath.Base(args[0])
  180. if debugNoExit {
  181. freeze := func(_ int) {
  182. // Infinite blocking op
  183. <-make(chan struct{})
  184. }
  185. // Override the logger os.Exit()
  186. logger.ExitFunc = freeze
  187. defer func() {
  188. if err := recover(); err != nil {
  189. fmt.Println("panic:", err)
  190. fmt.Println("")
  191. fmt.Println(string(debug.Stack()))
  192. }
  193. freeze(-1)
  194. }()
  195. }
  196. // Run the app - exit on error.
  197. if err := newApp(appName).Run(args); err != nil {
  198. os.Exit(1) //nolint:gocritic
  199. }
  200. }