github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/server-main.go (about)

     1  // Copyright (c) 2015-2024 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  
    18  package cmd
    19  
    20  import (
    21  	"context"
    22  	"encoding/hex"
    23  	"errors"
    24  	"fmt"
    25  	"io"
    26  	"log"
    27  	"math/rand"
    28  	"net"
    29  	"os"
    30  	"os/signal"
    31  	"runtime"
    32  	"strings"
    33  	"syscall"
    34  	"time"
    35  
    36  	"github.com/coreos/go-systemd/v22/daemon"
    37  	"github.com/minio/cli"
    38  	"github.com/minio/madmin-go/v3"
    39  	"github.com/minio/minio-go/v7"
    40  	"github.com/minio/minio-go/v7/pkg/credentials"
    41  	"github.com/minio/minio-go/v7/pkg/set"
    42  	"github.com/minio/minio/internal/auth"
    43  	"github.com/minio/minio/internal/bucket/bandwidth"
    44  	"github.com/minio/minio/internal/color"
    45  	"github.com/minio/minio/internal/config"
    46  	"github.com/minio/minio/internal/handlers"
    47  	"github.com/minio/minio/internal/hash/sha256"
    48  	xhttp "github.com/minio/minio/internal/http"
    49  	xioutil "github.com/minio/minio/internal/ioutil"
    50  	"github.com/minio/minio/internal/logger"
    51  	"github.com/minio/pkg/v2/certs"
    52  	"github.com/minio/pkg/v2/env"
    53  	"golang.org/x/exp/slices"
    54  	"gopkg.in/yaml.v2"
    55  )
    56  
    57  // ServerFlags - server command specific flags
    58  var ServerFlags = []cli.Flag{
    59  	cli.StringFlag{
    60  		Name:   "config",
    61  		Usage:  "specify server configuration via YAML configuration",
    62  		EnvVar: "MINIO_CONFIG",
    63  	},
    64  	cli.StringFlag{
    65  		Name:   "address",
    66  		Value:  ":" + GlobalMinioDefaultPort,
    67  		Usage:  "bind to a specific ADDRESS:PORT, ADDRESS can be an IP or hostname",
    68  		EnvVar: "MINIO_ADDRESS",
    69  	},
    70  	cli.IntFlag{
    71  		Name:   "listeners", // Deprecated Oct 2022
    72  		Value:  1,
    73  		Usage:  "bind N number of listeners per ADDRESS:PORT",
    74  		EnvVar: "MINIO_LISTENERS",
    75  		Hidden: true,
    76  	},
    77  	cli.StringFlag{
    78  		Name:   "console-address",
    79  		Usage:  "bind to a specific ADDRESS:PORT for embedded Console UI, ADDRESS can be an IP or hostname",
    80  		EnvVar: "MINIO_CONSOLE_ADDRESS",
    81  	},
    82  	cli.DurationFlag{
    83  		Name:   "shutdown-timeout",
    84  		Value:  xhttp.DefaultShutdownTimeout,
    85  		Usage:  "shutdown timeout to gracefully shutdown server",
    86  		EnvVar: "MINIO_SHUTDOWN_TIMEOUT",
    87  		Hidden: true,
    88  	},
    89  	cli.DurationFlag{
    90  		Name:   "idle-timeout",
    91  		Value:  xhttp.DefaultIdleTimeout,
    92  		Usage:  "idle timeout is the maximum amount of time to wait for the next request when keep-alive are enabled",
    93  		EnvVar: "MINIO_IDLE_TIMEOUT",
    94  		Hidden: true,
    95  	},
    96  	cli.DurationFlag{
    97  		Name:   "read-header-timeout",
    98  		Value:  xhttp.DefaultReadHeaderTimeout,
    99  		Usage:  "read header timeout is the amount of time allowed to read request headers",
   100  		EnvVar: "MINIO_READ_HEADER_TIMEOUT",
   101  		Hidden: true,
   102  	},
   103  	cli.DurationFlag{
   104  		Name:   "conn-client-read-deadline",
   105  		Usage:  "custom connection READ deadline for incoming requests",
   106  		Hidden: true,
   107  		EnvVar: "MINIO_CONN_CLIENT_READ_DEADLINE",
   108  	},
   109  	cli.DurationFlag{
   110  		Name:   "conn-client-write-deadline",
   111  		Usage:  "custom connection WRITE deadline for outgoing requests",
   112  		Hidden: true,
   113  		EnvVar: "MINIO_CONN_CLIENT_WRITE_DEADLINE",
   114  	},
   115  	cli.DurationFlag{
   116  		Name:   "conn-read-deadline",
   117  		Usage:  "custom connection READ deadline",
   118  		Hidden: true,
   119  		Value:  10 * time.Minute,
   120  		EnvVar: "MINIO_CONN_READ_DEADLINE",
   121  	},
   122  	cli.DurationFlag{
   123  		Name:   "conn-write-deadline",
   124  		Usage:  "custom connection WRITE deadline",
   125  		Hidden: true,
   126  		Value:  10 * time.Minute,
   127  		EnvVar: "MINIO_CONN_WRITE_DEADLINE",
   128  	},
   129  	cli.DurationFlag{
   130  		Name:   "conn-user-timeout",
   131  		Usage:  "custom TCP_USER_TIMEOUT for socket buffers",
   132  		Hidden: true,
   133  		Value:  10 * time.Minute,
   134  		EnvVar: "MINIO_CONN_USER_TIMEOUT",
   135  	},
   136  	cli.StringFlag{
   137  		Name:   "interface",
   138  		Usage:  "bind to right VRF device for MinIO services",
   139  		Hidden: true,
   140  		EnvVar: "MINIO_INTERFACE",
   141  	},
   142  	cli.DurationFlag{
   143  		Name:   "dns-cache-ttl",
   144  		Usage:  "custom DNS cache TTL",
   145  		Hidden: true,
   146  		Value: func() time.Duration {
   147  			if orchestrated {
   148  				return 30 * time.Second
   149  			}
   150  			return 10 * time.Minute
   151  		}(),
   152  		EnvVar: "MINIO_DNS_CACHE_TTL",
   153  	},
   154  	cli.IntFlag{
   155  		Name:   "max-idle-conns-per-host",
   156  		Usage:  "set a custom max idle connections per host value",
   157  		Hidden: true,
   158  		Value:  2048,
   159  		EnvVar: "MINIO_MAX_IDLE_CONNS_PER_HOST",
   160  	},
   161  	cli.StringSliceFlag{
   162  		Name:  "ftp",
   163  		Usage: "enable and configure an FTP(Secure) server",
   164  	},
   165  	cli.StringSliceFlag{
   166  		Name:  "sftp",
   167  		Usage: "enable and configure an SFTP server",
   168  	},
   169  	cli.StringFlag{
   170  		Name:   "crossdomain-xml",
   171  		Usage:  "provide a custom crossdomain-xml configuration to report at http://endpoint/crossdomain.xml",
   172  		Hidden: true,
   173  		EnvVar: "MINIO_CROSSDOMAIN_XML",
   174  	},
   175  }
   176  
   177  var gatewayCmd = cli.Command{
   178  	Name:            "gateway",
   179  	Usage:           "start object storage gateway",
   180  	Hidden:          true,
   181  	Flags:           append(ServerFlags, GlobalFlags...),
   182  	HideHelpCommand: true,
   183  	Action:          gatewayMain,
   184  }
   185  
   186  func gatewayMain(ctx *cli.Context) error {
   187  	logger.Fatal(errInvalidArgument, "Gateway is deprecated, To continue to use Gateway please use releases no later than 'RELEASE.2022-10-24T18-35-07Z'. We recommend all our users to migrate from gateway mode to server mode. Please read https://blog.min.io/deprecation-of-the-minio-gateway/")
   188  	return nil
   189  }
   190  
   191  var serverCmd = cli.Command{
   192  	Name:   "server",
   193  	Usage:  "start object storage server",
   194  	Flags:  append(ServerFlags, GlobalFlags...),
   195  	Action: serverMain,
   196  	CustomHelpTemplate: `NAME:
   197    {{.HelpName}} - {{.Usage}}
   198  
   199  USAGE:
   200    {{.HelpName}} {{if .VisibleFlags}}[FLAGS] {{end}}DIR1 [DIR2..]
   201    {{.HelpName}} {{if .VisibleFlags}}[FLAGS] {{end}}DIR{1...64}
   202    {{.HelpName}} {{if .VisibleFlags}}[FLAGS] {{end}}DIR{1...64} DIR{65...128}
   203  
   204  DIR:
   205    DIR points to a directory on a filesystem. When you want to combine
   206    multiple drives into a single large system, pass one directory per
   207    filesystem separated by space. You may also use a '...' convention
   208    to abbreviate the directory arguments. Remote directories in a
   209    distributed setup are encoded as HTTP(s) URIs.
   210  {{if .VisibleFlags}}
   211  FLAGS:
   212    {{range .VisibleFlags}}{{.}}
   213    {{end}}{{end}}
   214  EXAMPLES:
   215    1. Start MinIO server on "/home/shared" directory.
   216       {{.Prompt}} {{.HelpName}} /home/shared
   217  
   218    2. Start single node server with 64 local drives "/mnt/data1" to "/mnt/data64".
   219       {{.Prompt}} {{.HelpName}} /mnt/data{1...64}
   220  
   221    3. Start distributed MinIO server on an 32 node setup with 32 drives each, run following command on all the nodes
   222       {{.Prompt}} {{.HelpName}} http://node{1...32}.example.com/mnt/export{1...32}
   223  
   224    4. Start distributed MinIO server in an expanded setup, run the following command on all the nodes
   225       {{.Prompt}} {{.HelpName}} http://node{1...16}.example.com/mnt/export{1...32} \
   226              http://node{17...64}.example.com/mnt/export{1...64}
   227  
   228    5. Start distributed MinIO server, with FTP and SFTP servers on all interfaces via port 8021, 8022 respectively
   229       {{.Prompt}} {{.HelpName}} http://node{1...4}.example.com/mnt/export{1...4} \
   230             --ftp="address=:8021" --ftp="passive-port-range=30000-40000" \
   231             --sftp="address=:8022" --sftp="ssh-private-key=${HOME}/.ssh/id_rsa"
   232  `,
   233  }
   234  
   235  func serverCmdArgs(ctx *cli.Context) []string {
   236  	v, _, _, err := env.LookupEnv(config.EnvArgs)
   237  	if err != nil {
   238  		logger.FatalIf(err, "Unable to validate passed arguments in %s:%s",
   239  			config.EnvArgs, os.Getenv(config.EnvArgs))
   240  	}
   241  	if v == "" {
   242  		v, _, _, err = env.LookupEnv(config.EnvVolumes)
   243  		if err != nil {
   244  			logger.FatalIf(err, "Unable to validate passed arguments in %s:%s",
   245  				config.EnvVolumes, os.Getenv(config.EnvVolumes))
   246  		}
   247  	}
   248  	if v == "" {
   249  		// Fall back to older environment value MINIO_ENDPOINTS
   250  		v, _, _, err = env.LookupEnv(config.EnvEndpoints)
   251  		if err != nil {
   252  			logger.FatalIf(err, "Unable to validate passed arguments in %s:%s",
   253  				config.EnvEndpoints, os.Getenv(config.EnvEndpoints))
   254  		}
   255  	}
   256  	if v == "" {
   257  		if !ctx.Args().Present() || ctx.Args().First() == "help" {
   258  			cli.ShowCommandHelpAndExit(ctx, ctx.Command.Name, 1)
   259  		}
   260  		return ctx.Args()
   261  	}
   262  	return strings.Fields(v)
   263  }
   264  
   265  func mergeServerCtxtFromConfigFile(configFile string, ctxt *serverCtxt) error {
   266  	rd, err := Open(configFile)
   267  	if err != nil {
   268  		return err
   269  	}
   270  	defer rd.Close()
   271  
   272  	cf := &config.ServerConfig{}
   273  	dec := yaml.NewDecoder(rd)
   274  	dec.SetStrict(true)
   275  	if err = dec.Decode(cf); err != nil {
   276  		return err
   277  	}
   278  	if cf.Version != "v1" {
   279  		return fmt.Errorf("unexpected version: %s", cf.Version)
   280  	}
   281  
   282  	ctxt.RootUser = cf.RootUser
   283  	ctxt.RootPwd = cf.RootPwd
   284  
   285  	if cf.Addr != "" {
   286  		ctxt.Addr = cf.Addr
   287  	}
   288  	if cf.ConsoleAddr != "" {
   289  		ctxt.ConsoleAddr = cf.ConsoleAddr
   290  	}
   291  	if cf.CertsDir != "" {
   292  		ctxt.CertsDir = cf.CertsDir
   293  		ctxt.certsDirSet = true
   294  	}
   295  
   296  	if cf.Options.FTP.Address != "" {
   297  		ctxt.FTP = append(ctxt.FTP, fmt.Sprintf("address=%s", cf.Options.FTP.Address))
   298  	}
   299  	if cf.Options.FTP.PassivePortRange != "" {
   300  		ctxt.FTP = append(ctxt.FTP, fmt.Sprintf("passive-port-range=%s", cf.Options.FTP.PassivePortRange))
   301  	}
   302  
   303  	if cf.Options.SFTP.Address != "" {
   304  		ctxt.SFTP = append(ctxt.SFTP, fmt.Sprintf("address=%s", cf.Options.SFTP.Address))
   305  	}
   306  	if cf.Options.SFTP.SSHPrivateKey != "" {
   307  		ctxt.SFTP = append(ctxt.SFTP, fmt.Sprintf("ssh-private-key=%s", cf.Options.SFTP.SSHPrivateKey))
   308  	}
   309  
   310  	ctxt.Layout, err = buildDisksLayoutFromConfFile(cf.Pools)
   311  	return err
   312  }
   313  
   314  func serverHandleCmdArgs(ctxt serverCtxt) {
   315  	handleCommonArgs(ctxt)
   316  
   317  	logger.FatalIf(CheckLocalServerAddr(globalMinioAddr), "Unable to validate passed arguments")
   318  
   319  	var err error
   320  	var setupType SetupType
   321  
   322  	// Check and load TLS certificates.
   323  	globalPublicCerts, globalTLSCerts, globalIsTLS, err = getTLSConfig()
   324  	logger.FatalIf(err, "Unable to load the TLS configuration")
   325  
   326  	// Check and load Root CAs.
   327  	globalRootCAs, err = certs.GetRootCAs(globalCertsCADir.Get())
   328  	logger.FatalIf(err, "Failed to read root CAs (%v)", err)
   329  
   330  	// Add the global public crts as part of global root CAs
   331  	for _, publicCrt := range globalPublicCerts {
   332  		globalRootCAs.AddCert(publicCrt)
   333  	}
   334  
   335  	// Register root CAs for remote ENVs
   336  	env.RegisterGlobalCAs(globalRootCAs)
   337  
   338  	globalEndpoints, setupType, err = createServerEndpoints(globalMinioAddr, ctxt.Layout.pools, ctxt.Layout.legacy)
   339  	logger.FatalIf(err, "Invalid command line arguments")
   340  	globalNodes = globalEndpoints.GetNodes()
   341  
   342  	globalIsErasure = (setupType == ErasureSetupType)
   343  	globalIsDistErasure = (setupType == DistErasureSetupType)
   344  	if globalIsDistErasure {
   345  		globalIsErasure = true
   346  	}
   347  	globalIsErasureSD = (setupType == ErasureSDSetupType)
   348  	if globalDynamicAPIPort && globalIsDistErasure {
   349  		logger.FatalIf(errInvalidArgument, "Invalid --address=\"%s\", port '0' is not allowed in a distributed erasure coded setup", ctxt.Addr)
   350  	}
   351  
   352  	globalLocalNodeName = GetLocalPeer(globalEndpoints, globalMinioHost, globalMinioPort)
   353  	nodeNameSum := sha256.Sum256([]byte(globalLocalNodeName))
   354  	globalLocalNodeNameHex = hex.EncodeToString(nodeNameSum[:])
   355  
   356  	// Initialize, see which NIC the service is running on, and save it as global value
   357  	setGlobalInternodeInterface(ctxt.Interface)
   358  
   359  	// allow transport to be HTTP/1.1 for proxying.
   360  	globalProxyTransport = NewCustomHTTPProxyTransport()()
   361  	globalProxyEndpoints = GetProxyEndpoints(globalEndpoints)
   362  	globalInternodeTransport = NewInternodeHTTPTransport(ctxt.MaxIdleConnsPerHost)()
   363  	globalRemoteTargetTransport = NewRemoteTargetHTTPTransport(false)()
   364  	globalHealthChkTransport = NewHTTPTransport()
   365  	globalForwarder = handlers.NewForwarder(&handlers.Forwarder{
   366  		PassHost:     true,
   367  		RoundTripper: NewHTTPTransportWithTimeout(1 * time.Hour),
   368  		Logger: func(err error) {
   369  			if err != nil && !errors.Is(err, context.Canceled) {
   370  				logger.LogIf(GlobalContext, err)
   371  			}
   372  		},
   373  	})
   374  
   375  	globalTCPOptions = xhttp.TCPOptions{
   376  		UserTimeout:        int(ctxt.UserTimeout.Milliseconds()),
   377  		ClientReadTimeout:  ctxt.ConnClientReadDeadline,
   378  		ClientWriteTimeout: ctxt.ConnClientWriteDeadline,
   379  		Interface:          ctxt.Interface,
   380  	}
   381  
   382  	// On macOS, if a process already listens on LOCALIPADDR:PORT, net.Listen() falls back
   383  	// to IPv6 address ie minio will start listening on IPv6 address whereas another
   384  	// (non-)minio process is listening on IPv4 of given port.
   385  	// To avoid this error situation we check for port availability.
   386  	logger.FatalIf(xhttp.CheckPortAvailability(globalMinioHost, globalMinioPort, globalTCPOptions), "Unable to start the server")
   387  
   388  	globalConnReadDeadline = ctxt.ConnReadDeadline
   389  	globalConnWriteDeadline = ctxt.ConnWriteDeadline
   390  }
   391  
   392  func initAllSubsystems(ctx context.Context) {
   393  	// Initialize notification peer targets
   394  	globalNotificationSys = NewNotificationSys(globalEndpoints)
   395  
   396  	// Create new notification system
   397  	globalEventNotifier = NewEventNotifier(GlobalContext)
   398  
   399  	// Create new bucket metadata system.
   400  	if globalBucketMetadataSys == nil {
   401  		globalBucketMetadataSys = NewBucketMetadataSys()
   402  	} else {
   403  		// Reinitialize safely when testing.
   404  		globalBucketMetadataSys.Reset()
   405  	}
   406  
   407  	// Create the bucket bandwidth monitor
   408  	globalBucketMonitor = bandwidth.NewMonitor(ctx, uint64(totalNodeCount()))
   409  
   410  	// Create a new config system.
   411  	globalConfigSys = NewConfigSys()
   412  
   413  	// Create new IAM system.
   414  	globalIAMSys = NewIAMSys()
   415  
   416  	// Create new policy system.
   417  	globalPolicySys = NewPolicySys()
   418  
   419  	// Create new lifecycle system.
   420  	globalLifecycleSys = NewLifecycleSys()
   421  
   422  	// Create new bucket encryption subsystem
   423  	globalBucketSSEConfigSys = NewBucketSSEConfigSys()
   424  
   425  	// Create new bucket object lock subsystem
   426  	globalBucketObjectLockSys = NewBucketObjectLockSys()
   427  
   428  	// Create new bucket quota subsystem
   429  	globalBucketQuotaSys = NewBucketQuotaSys()
   430  
   431  	// Create new bucket versioning subsystem
   432  	if globalBucketVersioningSys == nil {
   433  		globalBucketVersioningSys = NewBucketVersioningSys()
   434  	}
   435  
   436  	// Create new bucket replication subsystem
   437  	globalBucketTargetSys = NewBucketTargetSys(GlobalContext)
   438  
   439  	// Create new ILM tier configuration subsystem
   440  	globalTierConfigMgr = NewTierConfigMgr()
   441  
   442  	globalTransitionState = newTransitionState(GlobalContext)
   443  	globalSiteResyncMetrics = newSiteResyncMetrics(GlobalContext)
   444  }
   445  
   446  func configRetriableErrors(err error) bool {
   447  	if err == nil {
   448  		return false
   449  	}
   450  
   451  	notInitialized := strings.Contains(err.Error(), "Server not initialized, please try again") ||
   452  		errors.Is(err, errServerNotInitialized)
   453  
   454  	// Initializing sub-systems needs a retry mechanism for
   455  	// the following reasons:
   456  	//  - Read quorum is lost just after the initialization
   457  	//    of the object layer.
   458  	//  - Write quorum not met when upgrading configuration
   459  	//    version is needed, migration is needed etc.
   460  	rquorum := InsufficientReadQuorum{}
   461  	wquorum := InsufficientWriteQuorum{}
   462  
   463  	// One of these retriable errors shall be retried.
   464  	return errors.Is(err, errDiskNotFound) ||
   465  		errors.Is(err, errConfigNotFound) ||
   466  		errors.Is(err, context.DeadlineExceeded) ||
   467  		errors.Is(err, errErasureWriteQuorum) ||
   468  		errors.Is(err, errErasureReadQuorum) ||
   469  		errors.Is(err, io.ErrUnexpectedEOF) ||
   470  		errors.As(err, &rquorum) ||
   471  		errors.As(err, &wquorum) ||
   472  		isErrObjectNotFound(err) ||
   473  		isErrBucketNotFound(err) ||
   474  		errors.Is(err, os.ErrDeadlineExceeded) ||
   475  		notInitialized
   476  }
   477  
   478  func bootstrapTraceMsg(msg string) {
   479  	info := madmin.TraceInfo{
   480  		TraceType: madmin.TraceBootstrap,
   481  		Time:      UTCNow(),
   482  		NodeName:  globalLocalNodeName,
   483  		FuncName:  "BOOTSTRAP",
   484  		Message:   fmt.Sprintf("%s %s", getSource(2), msg),
   485  	}
   486  	globalBootstrapTracer.Record(info)
   487  
   488  	if serverDebugLog {
   489  		fmt.Println(time.Now().Round(time.Millisecond).Format(time.RFC3339), " bootstrap: ", msg)
   490  	}
   491  
   492  	noSubs := globalTrace.NumSubscribers(madmin.TraceBootstrap) == 0
   493  	if noSubs {
   494  		return
   495  	}
   496  
   497  	globalTrace.Publish(info)
   498  }
   499  
   500  func bootstrapTrace(msg string, worker func()) {
   501  	if serverDebugLog {
   502  		fmt.Println(time.Now().Round(time.Millisecond).Format(time.RFC3339), " bootstrap: ", msg)
   503  	}
   504  
   505  	now := time.Now()
   506  	worker()
   507  	dur := time.Since(now)
   508  
   509  	info := madmin.TraceInfo{
   510  		TraceType: madmin.TraceBootstrap,
   511  		Time:      UTCNow(),
   512  		NodeName:  globalLocalNodeName,
   513  		FuncName:  "BOOTSTRAP",
   514  		Message:   fmt.Sprintf("%s %s (duration: %s)", getSource(2), msg, dur),
   515  	}
   516  	globalBootstrapTracer.Record(info)
   517  
   518  	if globalTrace.NumSubscribers(madmin.TraceBootstrap) == 0 {
   519  		return
   520  	}
   521  
   522  	globalTrace.Publish(info)
   523  }
   524  
   525  func initServerConfig(ctx context.Context, newObject ObjectLayer) error {
   526  	t1 := time.Now()
   527  
   528  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   529  
   530  	for {
   531  		select {
   532  		case <-ctx.Done():
   533  			// Retry was canceled successfully.
   534  			return fmt.Errorf("Initializing sub-systems stopped gracefully %w", ctx.Err())
   535  		default:
   536  		}
   537  
   538  		// These messages only meant primarily for distributed setup, so only log during distributed setup.
   539  		if globalIsDistErasure {
   540  			logger.Info("Waiting for all MinIO sub-systems to be initialize...")
   541  		}
   542  
   543  		// Upon success migrating the config, initialize all sub-systems
   544  		// if all sub-systems initialized successfully return right away
   545  		err := initConfigSubsystem(ctx, newObject)
   546  		if err == nil {
   547  			// All successful return.
   548  			if globalIsDistErasure {
   549  				// These messages only meant primarily for distributed setup, so only log during distributed setup.
   550  				logger.Info("All MinIO sub-systems initialized successfully in %s", time.Since(t1))
   551  			}
   552  			return nil
   553  		}
   554  
   555  		if configRetriableErrors(err) {
   556  			logger.Info("Waiting for all MinIO sub-systems to be initialized.. possible cause (%v)", err)
   557  			time.Sleep(time.Duration(r.Float64() * float64(5*time.Second)))
   558  			continue
   559  		}
   560  
   561  		// Any other unhandled return right here.
   562  		return fmt.Errorf("Unable to initialize sub-systems: %w", err)
   563  	}
   564  }
   565  
   566  func initConfigSubsystem(ctx context.Context, newObject ObjectLayer) error {
   567  	// %w is used by all error returns here to make sure
   568  	// we wrap the underlying error, make sure when you
   569  	// are modifying this code that you do so, if and when
   570  	// you want to add extra context to your error. This
   571  	// ensures top level retry works accordingly.
   572  
   573  	// Initialize config system.
   574  	if err := globalConfigSys.Init(newObject); err != nil {
   575  		if configRetriableErrors(err) {
   576  			return fmt.Errorf("Unable to initialize config system: %w", err)
   577  		}
   578  
   579  		// Any other config errors we simply print a message and proceed forward.
   580  		logger.LogIf(ctx, fmt.Errorf("Unable to initialize config, some features may be missing: %w", err))
   581  	}
   582  
   583  	return nil
   584  }
   585  
   586  func setGlobalInternodeInterface(interfaceName string) {
   587  	globalInternodeInterfaceOnce.Do(func() {
   588  		if interfaceName != "" {
   589  			globalInternodeInterface = interfaceName
   590  			return
   591  		}
   592  		ip := "127.0.0.1"
   593  		host, _ := mustSplitHostPort(globalLocalNodeName)
   594  		if host != "" {
   595  			if net.ParseIP(host) != nil {
   596  				ip = host
   597  			} else {
   598  				ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
   599  				defer cancel()
   600  
   601  				haddrs, err := globalDNSCache.LookupHost(ctx, host)
   602  				if err == nil {
   603  					ip = haddrs[0]
   604  				}
   605  			}
   606  		}
   607  		ifs, _ := net.Interfaces()
   608  		for _, interf := range ifs {
   609  			addrs, err := interf.Addrs()
   610  			if err == nil {
   611  				for _, addr := range addrs {
   612  					if strings.SplitN(addr.String(), "/", 2)[0] == ip {
   613  						globalInternodeInterface = interf.Name
   614  					}
   615  				}
   616  			}
   617  		}
   618  	})
   619  }
   620  
   621  // Return the list of address that MinIO server needs to listen on:
   622  //   - Returning 127.0.0.1 is necessary so Console will be able to send
   623  //     requests to the local S3 API.
   624  //   - The returned List needs to be deduplicated as well.
   625  func getServerListenAddrs() []string {
   626  	// Use a string set to avoid duplication
   627  	addrs := set.NewStringSet()
   628  	// Listen on local interface to receive requests from Console
   629  	for _, ip := range mustGetLocalIPs() {
   630  		if ip != nil && ip.IsLoopback() {
   631  			addrs.Add(net.JoinHostPort(ip.String(), globalMinioPort))
   632  		}
   633  	}
   634  	host, _ := mustSplitHostPort(globalMinioAddr)
   635  	if host != "" {
   636  		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
   637  		defer cancel()
   638  
   639  		haddrs, err := globalDNSCache.LookupHost(ctx, host)
   640  		if err == nil {
   641  			for _, addr := range haddrs {
   642  				addrs.Add(net.JoinHostPort(addr, globalMinioPort))
   643  			}
   644  		} else {
   645  			// Unable to lookup host in 2-secs, let it fail later anyways.
   646  			addrs.Add(globalMinioAddr)
   647  		}
   648  	} else {
   649  		addrs.Add(globalMinioAddr)
   650  	}
   651  	return addrs.ToSlice()
   652  }
   653  
   654  // serverMain handler called for 'minio server' command.
   655  func serverMain(ctx *cli.Context) {
   656  	var warnings []string
   657  
   658  	signal.Notify(globalOSSignalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
   659  
   660  	go handleSignals()
   661  
   662  	setDefaultProfilerRates()
   663  
   664  	// Initialize globalConsoleSys system
   665  	bootstrapTrace("newConsoleLogger", func() {
   666  		globalConsoleSys = NewConsoleLogger(GlobalContext)
   667  		logger.AddSystemTarget(GlobalContext, globalConsoleSys)
   668  
   669  		// Set node name, only set for distributed setup.
   670  		globalConsoleSys.SetNodeName(globalLocalNodeName)
   671  	})
   672  
   673  	// Always load ENV variables from files first.
   674  	loadEnvVarsFromFiles()
   675  
   676  	// Handle all server command args and build the disks layout
   677  	bootstrapTrace("serverHandleCmdArgs", func() {
   678  		err := buildServerCtxt(ctx, &globalServerCtxt)
   679  		logger.FatalIf(err, "Unable to prepare the list of endpoints")
   680  
   681  		serverHandleCmdArgs(globalServerCtxt)
   682  	})
   683  
   684  	// DNS cache subsystem to reduce outgoing DNS requests
   685  	runDNSCache(ctx)
   686  
   687  	// Handle all server environment vars.
   688  	serverHandleEnvVars()
   689  
   690  	// Load the root credentials from the shell environment or from
   691  	// the config file if not defined, set the default one.
   692  	loadRootCredentials()
   693  
   694  	// Perform any self-tests
   695  	bootstrapTrace("selftests", func() {
   696  		bitrotSelfTest()
   697  		erasureSelfTest()
   698  		compressSelfTest()
   699  	})
   700  
   701  	// Initialize KMS configuration
   702  	bootstrapTrace("handleKMSConfig", handleKMSConfig)
   703  
   704  	// Initialize all help
   705  	bootstrapTrace("initHelp", initHelp)
   706  
   707  	// Initialize all sub-systems
   708  	bootstrapTrace("initAllSubsystems", func() {
   709  		initAllSubsystems(GlobalContext)
   710  	})
   711  
   712  	// Is distributed setup, error out if no certificates are found for HTTPS endpoints.
   713  	if globalIsDistErasure {
   714  		if globalEndpoints.HTTPS() && !globalIsTLS {
   715  			logger.Fatal(config.ErrNoCertsAndHTTPSEndpoints(nil), "Unable to start the server")
   716  		}
   717  		if !globalEndpoints.HTTPS() && globalIsTLS {
   718  			logger.Fatal(config.ErrCertsAndHTTPEndpoints(nil), "Unable to start the server")
   719  		}
   720  	}
   721  
   722  	// Check for updates in non-blocking manner.
   723  	go func() {
   724  		if !globalServerCtxt.Quiet && !globalInplaceUpdateDisabled {
   725  			// Check for new updates from dl.min.io.
   726  			bootstrapTrace("checkUpdate", func() {
   727  				checkUpdate(getMinioMode())
   728  			})
   729  		}
   730  	}()
   731  
   732  	// Set system resources to maximum.
   733  	bootstrapTrace("setMaxResources", func() {
   734  		_ = setMaxResources()
   735  	})
   736  
   737  	// Verify kernel release and version.
   738  	if oldLinux() {
   739  		warnings = append(warnings, color.YellowBold("- Detected Linux kernel version older than 4.0.0 release, there are some known potential performance problems with this kernel version. MinIO recommends a minimum of 4.x.x linux kernel version for best performance"))
   740  	}
   741  
   742  	maxProcs := runtime.GOMAXPROCS(0)
   743  	cpuProcs := runtime.NumCPU()
   744  	if maxProcs < cpuProcs {
   745  		warnings = append(warnings, color.YellowBold("- Detected GOMAXPROCS(%d) < NumCPU(%d), please make sure to provide all PROCS to MinIO for optimal performance", maxProcs, cpuProcs))
   746  	}
   747  
   748  	var getCert certs.GetCertificateFunc
   749  	if globalTLSCerts != nil {
   750  		getCert = globalTLSCerts.GetCertificate
   751  	}
   752  
   753  	// Initialize gridn
   754  	bootstrapTrace("initGrid", func() {
   755  		logger.FatalIf(initGlobalGrid(GlobalContext, globalEndpoints), "Unable to configure server grid RPC services")
   756  	})
   757  
   758  	// Configure server.
   759  	bootstrapTrace("configureServer", func() {
   760  		handler, err := configureServerHandler(globalEndpoints)
   761  		if err != nil {
   762  			logger.Fatal(config.ErrUnexpectedError(err), "Unable to configure one of server's RPC services")
   763  		}
   764  		// Allow grid to start after registering all services.
   765  		xioutil.SafeClose(globalGridStart)
   766  
   767  		httpServer := xhttp.NewServer(getServerListenAddrs()).
   768  			UseHandler(setCriticalErrorHandler(corsHandler(handler))).
   769  			UseTLSConfig(newTLSConfig(getCert)).
   770  			UseShutdownTimeout(globalServerCtxt.ShutdownTimeout).
   771  			UseIdleTimeout(globalServerCtxt.IdleTimeout).
   772  			UseReadHeaderTimeout(globalServerCtxt.ReadHeaderTimeout).
   773  			UseBaseContext(GlobalContext).
   774  			UseCustomLogger(log.New(io.Discard, "", 0)). // Turn-off random logging by Go stdlib
   775  			UseTCPOptions(globalTCPOptions)
   776  
   777  		httpServer.TCPOptions.Trace = bootstrapTraceMsg
   778  		go func() {
   779  			serveFn, err := httpServer.Init(GlobalContext, func(listenAddr string, err error) {
   780  				logger.LogIf(GlobalContext, fmt.Errorf("Unable to listen on `%s`: %v", listenAddr, err))
   781  			})
   782  			if err != nil {
   783  				globalHTTPServerErrorCh <- err
   784  				return
   785  			}
   786  			globalHTTPServerErrorCh <- serveFn()
   787  		}()
   788  
   789  		setHTTPServer(httpServer)
   790  	})
   791  
   792  	if globalIsDistErasure {
   793  		bootstrapTrace("verifying system configuration", func() {
   794  			// Additionally in distributed setup, validate the setup and configuration.
   795  			if err := verifyServerSystemConfig(GlobalContext, globalEndpoints, globalGrid.Load()); err != nil {
   796  				logger.Fatal(err, "Unable to start the server")
   797  			}
   798  		})
   799  	}
   800  
   801  	if !globalDisableFreezeOnBoot {
   802  		// Freeze the services until the bucket notification subsystem gets initialized.
   803  		bootstrapTrace("freezeServices", freezeServices)
   804  	}
   805  
   806  	var newObject ObjectLayer
   807  	bootstrapTrace("newObjectLayer", func() {
   808  		var err error
   809  		newObject, err = newObjectLayer(GlobalContext, globalEndpoints)
   810  		if err != nil {
   811  			logFatalErrs(err, Endpoint{}, true)
   812  		}
   813  	})
   814  
   815  	xhttp.SetDeploymentID(globalDeploymentID())
   816  	xhttp.SetMinIOVersion(Version)
   817  
   818  	for _, n := range globalNodes {
   819  		nodeName := n.Host
   820  		if n.IsLocal {
   821  			nodeName = globalLocalNodeName
   822  		}
   823  		nodeNameSum := sha256.Sum256([]byte(nodeName + globalDeploymentID()))
   824  		globalNodeNamesHex[hex.EncodeToString(nodeNameSum[:])] = struct{}{}
   825  	}
   826  
   827  	var err error
   828  	bootstrapTrace("initServerConfig", func() {
   829  		if err = initServerConfig(GlobalContext, newObject); err != nil {
   830  			var cerr config.Err
   831  			// For any config error, we don't need to drop into safe-mode
   832  			// instead its a user error and should be fixed by user.
   833  			if errors.As(err, &cerr) {
   834  				logger.FatalIf(err, "Unable to initialize the server")
   835  			}
   836  
   837  			// If context was canceled
   838  			if errors.Is(err, context.Canceled) {
   839  				logger.FatalIf(err, "Server startup canceled upon user request")
   840  			}
   841  
   842  			logger.LogIf(GlobalContext, err)
   843  		}
   844  
   845  		if !globalServerCtxt.StrictS3Compat {
   846  			warnings = append(warnings, color.YellowBold("- Strict AWS S3 compatible incoming PUT, POST content payload validation is turned off, caution is advised do not use in production"))
   847  		}
   848  	})
   849  	if globalActiveCred.Equal(auth.DefaultCredentials) {
   850  		msg := fmt.Sprintf("- Detected default credentials '%s', we recommend that you change these values with 'MINIO_ROOT_USER' and 'MINIO_ROOT_PASSWORD' environment variables",
   851  			globalActiveCred)
   852  		warnings = append(warnings, color.YellowBold(msg))
   853  	}
   854  
   855  	// Initialize users credentials and policies in background right after config has initialized.
   856  	go func() {
   857  		bootstrapTrace("globalIAMSys.Init", func() {
   858  			globalIAMSys.Init(GlobalContext, newObject, globalEtcdClient, globalRefreshIAMInterval)
   859  		})
   860  
   861  		// Initialize Console UI
   862  		if globalBrowserEnabled {
   863  			bootstrapTrace("initConsoleServer", func() {
   864  				srv, err := initConsoleServer()
   865  				if err != nil {
   866  					logger.FatalIf(err, "Unable to initialize console service")
   867  				}
   868  
   869  				setConsoleSrv(srv)
   870  
   871  				go func() {
   872  					logger.FatalIf(newConsoleServerFn().Serve(), "Unable to initialize console server")
   873  				}()
   874  			})
   875  		}
   876  
   877  		// if we see FTP args, start FTP if possible
   878  		if len(globalServerCtxt.FTP) > 0 {
   879  			bootstrapTrace("go startFTPServer", func() {
   880  				go startFTPServer(globalServerCtxt.FTP)
   881  			})
   882  		}
   883  
   884  		// If we see SFTP args, start SFTP if possible
   885  		if len(globalServerCtxt.SFTP) > 0 {
   886  			bootstrapTrace("go startSFTPServer", func() {
   887  				go startSFTPServer(globalServerCtxt.SFTP)
   888  			})
   889  		}
   890  	}()
   891  
   892  	go func() {
   893  		r := rand.New(rand.NewSource(time.Now().UnixNano()))
   894  
   895  		if !globalDisableFreezeOnBoot {
   896  			defer bootstrapTrace("unfreezeServices", unfreezeServices)
   897  			t := time.AfterFunc(5*time.Minute, func() {
   898  				warnings = append(warnings, color.YellowBold("- Initializing the config subsystem is taking longer than 5 minutes. Please set '_MINIO_DISABLE_API_FREEZE_ON_BOOT=true' to not freeze the APIs"))
   899  			})
   900  			defer t.Stop()
   901  		}
   902  
   903  		// Initialize data scanner.
   904  		bootstrapTrace("initDataScanner", func() {
   905  			if v := env.Get("_MINIO_SCANNER", config.EnableOn); v == config.EnableOn {
   906  				initDataScanner(GlobalContext, newObject)
   907  			}
   908  		})
   909  
   910  		// Initialize background replication
   911  		bootstrapTrace("initBackgroundReplication", func() {
   912  			initBackgroundReplication(GlobalContext, newObject)
   913  		})
   914  
   915  		// Initialize background ILM worker poool
   916  		bootstrapTrace("initBackgroundExpiry", func() {
   917  			initBackgroundExpiry(GlobalContext, newObject)
   918  		})
   919  
   920  		bootstrapTrace("globalTransitionState.Init", func() {
   921  			globalTransitionState.Init(newObject)
   922  		})
   923  
   924  		// Initialize batch job pool.
   925  		bootstrapTrace("newBatchJobPool", func() {
   926  			globalBatchJobPool = newBatchJobPool(GlobalContext, newObject, 100)
   927  		})
   928  
   929  		// Initialize the license update job
   930  		bootstrapTrace("initLicenseUpdateJob", func() {
   931  			initLicenseUpdateJob(GlobalContext, newObject)
   932  		})
   933  
   934  		go func() {
   935  			// Initialize transition tier configuration manager
   936  			bootstrapTrace("globalTierConfigMgr.Init", func() {
   937  				if err := globalTierConfigMgr.Init(GlobalContext, newObject); err != nil {
   938  					logger.LogIf(GlobalContext, err)
   939  				}
   940  			})
   941  		}()
   942  
   943  		// Initialize bucket notification system.
   944  		bootstrapTrace("initBucketTargets", func() {
   945  			logger.LogIf(GlobalContext, globalEventNotifier.InitBucketTargets(GlobalContext, newObject))
   946  		})
   947  
   948  		var buckets []BucketInfo
   949  		// List buckets to initialize bucket metadata sub-sys.
   950  		bootstrapTrace("listBuckets", func() {
   951  			for {
   952  				buckets, err = newObject.ListBuckets(GlobalContext, BucketOptions{})
   953  				if err != nil {
   954  					if configRetriableErrors(err) {
   955  						logger.Info("Waiting for list buckets to succeed to initialize buckets.. possible cause (%v)", err)
   956  						time.Sleep(time.Duration(r.Float64() * float64(time.Second)))
   957  						continue
   958  					}
   959  					logger.LogIf(GlobalContext, fmt.Errorf("Unable to list buckets to initialize bucket metadata sub-system: %w", err))
   960  				}
   961  
   962  				break
   963  			}
   964  		})
   965  
   966  		// Initialize bucket metadata sub-system.
   967  		bootstrapTrace("globalBucketMetadataSys.Init", func() {
   968  			globalBucketMetadataSys.Init(GlobalContext, buckets, newObject)
   969  		})
   970  
   971  		// initialize replication resync state.
   972  		bootstrapTrace("initResync", func() {
   973  			globalReplicationPool.initResync(GlobalContext, buckets, newObject)
   974  		})
   975  
   976  		// Initialize site replication manager after bucket metadata
   977  		bootstrapTrace("globalSiteReplicationSys.Init", func() {
   978  			globalSiteReplicationSys.Init(GlobalContext, newObject)
   979  		})
   980  
   981  		// Initialize quota manager.
   982  		bootstrapTrace("globalBucketQuotaSys.Init", func() {
   983  			globalBucketQuotaSys.Init(newObject)
   984  		})
   985  
   986  		// Populate existing buckets to the etcd backend
   987  		if globalDNSConfig != nil {
   988  			// Background this operation.
   989  			bootstrapTrace("go initFederatorBackend", func() {
   990  				go initFederatorBackend(buckets, newObject)
   991  			})
   992  		}
   993  
   994  		// Prints the formatted startup message, if err is not nil then it prints additional information as well.
   995  		printStartupMessage(getAPIEndpoints(), err)
   996  
   997  		// Print a warning at the end of the startup banner so it is more noticeable
   998  		if newObject.BackendInfo().StandardSCParity == 0 {
   999  			warnings = append(warnings, color.YellowBold("- The standard parity is set to 0. This can lead to data loss."))
  1000  		}
  1001  		objAPI := newObjectLayerFn()
  1002  		if objAPI != nil {
  1003  			printStorageInfo(objAPI.StorageInfo(GlobalContext, true))
  1004  		}
  1005  		if len(warnings) > 0 {
  1006  			logger.Info(color.Yellow("STARTUP WARNINGS:"))
  1007  			for _, warn := range warnings {
  1008  				logger.Info(warn)
  1009  			}
  1010  		}
  1011  	}()
  1012  
  1013  	region := globalSite.Region
  1014  	if region == "" {
  1015  		region = "us-east-1"
  1016  	}
  1017  	bootstrapTrace("globalMinioClient", func() {
  1018  		globalMinioClient, err = minio.New(globalLocalNodeName, &minio.Options{
  1019  			Creds:     credentials.NewStaticV4(globalActiveCred.AccessKey, globalActiveCred.SecretKey, ""),
  1020  			Secure:    globalIsTLS,
  1021  			Transport: globalProxyTransport,
  1022  			Region:    region,
  1023  		})
  1024  		logger.FatalIf(err, "Unable to initialize MinIO client")
  1025  	})
  1026  
  1027  	go bootstrapTrace("startResourceMetricsCollection", func() {
  1028  		startResourceMetricsCollection()
  1029  	})
  1030  
  1031  	// Add User-Agent to differentiate the requests.
  1032  	globalMinioClient.SetAppInfo("minio-perf-test", ReleaseTag)
  1033  
  1034  	if serverDebugLog {
  1035  		fmt.Println("== DEBUG Mode enabled ==")
  1036  		fmt.Println("Currently set environment settings:")
  1037  		ks := []string{
  1038  			config.EnvAccessKey,
  1039  			config.EnvSecretKey,
  1040  			config.EnvRootUser,
  1041  			config.EnvRootPassword,
  1042  		}
  1043  		for _, v := range os.Environ() {
  1044  			// Do not print sensitive creds in debug.
  1045  			if slices.Contains(ks, strings.Split(v, "=")[0]) {
  1046  				continue
  1047  			}
  1048  			fmt.Println(v)
  1049  		}
  1050  		fmt.Println("======")
  1051  	}
  1052  
  1053  	daemon.SdNotify(false, daemon.SdNotifyReady)
  1054  
  1055  	<-globalOSSignalCh
  1056  }
  1057  
  1058  // Initialize object layer with the supplied disks, objectLayer is nil upon any error.
  1059  func newObjectLayer(ctx context.Context, endpointServerPools EndpointServerPools) (newObject ObjectLayer, err error) {
  1060  	return newErasureServerPools(ctx, endpointServerPools)
  1061  }