Beispiel #1
0
// checkShareDownloadSyntax - validate command-line args.
func checkShareDownloadSyntax(ctx *cli.Context) {
	args := ctx.Args()
	if !args.Present() {
		cli.ShowCommandHelpAndExit(ctx, "download", 1) // last argument is exit code.
	}

	// Parse expiry.
	expiry := shareDefaultExpiry
	expireArg := ctx.String("expire")
	if expireArg != "" {
		var e error
		expiry, e = time.ParseDuration(expireArg)
		fatalIf(probe.NewError(e), "Unable to parse expire=‘"+expireArg+"’.")
	}

	// Validate expiry.
	if expiry.Seconds() < 1 {
		fatalIf(errDummy().Trace(expiry.String()), "Expiry cannot be lesser than 1 second.")
	}
	if expiry.Seconds() > 604800 {
		fatalIf(errDummy().Trace(expiry.String()), "Expiry cannot be larger than 7 days.")
	}

	for _, url := range ctx.Args() {
		_, _, err := url2Stat(url)
		fatalIf(err.Trace(url), "Unable to stat ‘"+url+"’.")
	}
}
Beispiel #2
0
// main for share upload command.
func mainShareUpload(ctx *cli.Context) {
	// Set global flags from context.
	setGlobalsFromContext(ctx)

	// check input arguments.
	checkShareUploadSyntax(ctx)

	// Initialize share config folder.
	initShareConfig()

	// Additional command speific theme customization.
	shareSetColor()

	// Set command flags from context.
	isRecursive := ctx.Bool("recursive")
	expireArg := ctx.String("expire")
	expiry := shareDefaultExpiry
	contentType := ctx.String("content-type")
	if expireArg != "" {
		var e error
		expiry, e = time.ParseDuration(expireArg)
		fatalIf(probe.NewError(e), "Unable to parse expire=‘"+expireArg+"’.")
	}

	for _, targetURL := range ctx.Args() {
		err := doShareUploadURL(targetURL, isRecursive, expiry, contentType)
		fatalIf(err.Trace(targetURL), "Unable to generate curl command for upload ‘"+targetURL+"’.")
	}
}
Beispiel #3
0
func mainEventsAdd(ctx *cli.Context) error {
	console.SetColor("Events", color.New(color.FgGreen, color.Bold))

	setGlobalsFromContext(ctx)
	checkEventsAddSyntax(ctx)

	args := ctx.Args()
	path := args[0]
	arn := args[1]

	events := strings.Split(ctx.String("events"), ",")
	prefix := ctx.String("prefix")
	suffix := ctx.String("suffix")

	client, err := newClient(path)
	if err != nil {
		fatalIf(err.Trace(), "Cannot parse the provided url.")
	}

	s3Client, ok := client.(*s3Client)
	if !ok {
		fatalIf(errDummy().Trace(), "The provided url doesn't point to a S3 server.")
	}

	err = s3Client.AddNotificationConfig(arn, events, prefix, suffix)
	fatalIf(err, "Cannot enable notification on the specified bucket.")
	printMsg(eventsAddMessage{
		ARN:    arn,
		Events: events,
		Prefix: prefix,
		Suffix: suffix,
	})

	return nil
}
Beispiel #4
0
func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
	var context *cli.Context

	a := cli.NewApp()
	a.Commands = []cli.Command{
		{
			Name: "foo",
			Action: func(c *cli.Context) {
				context = c
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "lang",
					Value: "english",
					Usage: "language for the greeting",
				},
			},
			Before: func(_ *cli.Context) error { return nil },
		},
	}
	a.Run([]string{"", "foo", "--lang", "spanish", "abcd"})

	expect(t, context.Args().Get(0), "abcd")
	expect(t, context.String("lang"), "spanish")
}
Beispiel #5
0
// Parse command arguments and set global variables accordingly
func setGlobalsFromContext(c *cli.Context) {
	// Set config dir
	switch {
	case c.IsSet("config-dir"):
		globalConfigDir = c.String("config-dir")
	case c.GlobalIsSet("config-dir"):
		globalConfigDir = c.GlobalString("config-dir")
	}
	if globalConfigDir == "" {
		console.Fatalf("Unable to get config file. Config directory is empty.")
	}
	// Set global quiet flag.
	globalQuiet = c.Bool("quiet") || c.GlobalBool("quiet")
}
Beispiel #6
0
// main for share download.
func mainShareDownload(ctx *cli.Context) error {
	// Set global flags from context.
	setGlobalsFromContext(ctx)

	// check input arguments.
	checkShareDownloadSyntax(ctx)

	// Initialize share config folder.
	initShareConfig()

	// Additional command speific theme customization.
	shareSetColor()

	// Set command flags from context.
	isRecursive := ctx.Bool("recursive")
	expiry := shareDefaultExpiry
	if ctx.String("expire") != "" {
		var e error
		expiry, e = time.ParseDuration(ctx.String("expire"))
		fatalIf(probe.NewError(e), "Unable to parse expire=‘"+ctx.String("expire")+"’.")
	}

	for _, targetURL := range ctx.Args() {
		err := doShareDownloadURL(targetURL, isRecursive, expiry)
		if err != nil {
			switch err.ToGoError().(type) {
			case APINotImplemented:
				fatalIf(err.Trace(), "Unable to share a non S3 url ‘"+targetURL+"’.")
			default:
				fatalIf(err.Trace(targetURL), "Unable to share target ‘"+targetURL+"’.")
			}
		}
	}
	return nil
}
Beispiel #7
0
// mainMakeBucket is entry point for mb command.
func mainMakeBucket(ctx *cli.Context) error {
	// Set global flags from context.
	setGlobalsFromContext(ctx)

	// check 'mb' cli arguments.
	checkMakeBucketSyntax(ctx)

	// Additional command speific theme customization.
	console.SetColor("MakeBucket", color.New(color.FgGreen, color.Bold))

	// Save region.
	region := ctx.String("region")

	var cErr error
	for i := range ctx.Args() {
		targetURL := ctx.Args().Get(i)
		// Instantiate client for URL.
		clnt, err := newClient(targetURL)
		if err != nil {
			errorIf(err.Trace(targetURL), "Invalid target ‘"+targetURL+"’.")
			cErr = exitStatus(globalErrorExitStatus)
			continue
		}

		// Make bucket.
		err = clnt.MakeBucket(region)
		if err != nil {
			errorIf(err.Trace(targetURL), "Unable to make bucket ‘"+targetURL+"’.")
			cErr = exitStatus(globalErrorExitStatus)
			continue
		}

		// Successfully created a bucket.
		printMsg(makeBucketMessage{Status: "success", Bucket: targetURL})
	}
	return cErr
}
Beispiel #8
0
// checkShareUploadSyntax - validate command-line args.
func checkShareUploadSyntax(ctx *cli.Context) {
	args := ctx.Args()
	if !args.Present() {
		cli.ShowCommandHelpAndExit(ctx, "upload", 1) // last argument is exit code.
	}

	// Set command flags from context.
	isRecursive := ctx.Bool("recursive")
	expireArg := ctx.String("expire")

	// Parse expiry.
	expiry := shareDefaultExpiry
	if expireArg != "" {
		var e error
		expiry, e = time.ParseDuration(expireArg)
		fatalIf(probe.NewError(e), "Unable to parse expire=‘"+expireArg+"’.")
	}

	// Validate expiry.
	if expiry.Seconds() < 1 {
		fatalIf(errDummy().Trace(expiry.String()),
			"Expiry cannot be lesser than 1 second.")
	}
	if expiry.Seconds() > 604800 {
		fatalIf(errDummy().Trace(expiry.String()),
			"Expiry cannot be larger than 7 days.")
	}

	for _, targetURL := range ctx.Args() {
		url := client.NewURL(targetURL)
		if strings.HasSuffix(targetURL, string(url.Separator)) && !isRecursive {
			fatalIf(errInvalidArgument().Trace(targetURL),
				"Use --recursive option to generate curl command for prefixes.")
		}
	}
}
Beispiel #9
0
// serverMain handler called for 'minio server' command.
func serverMain(c *cli.Context) {
	if !c.Args().Present() || c.Args().First() == "help" {
		cli.ShowCommandHelpAndExit(c, "server", 1)
	}

	// Set global variables after parsing passed arguments
	setGlobalsFromContext(c)

	// Initialization routine, such as config loading, enable logging, ..
	minioInit()

	// Check for minio updates from dl.minio.io
	checkUpdate()

	// Server address.
	serverAddr := c.String("address")

	// Check if requested port is available.
	host, portStr, err := net.SplitHostPort(serverAddr)
	fatalIf(err, "Unable to parse %s.", serverAddr)
	if portStr == "0" || portStr == "" {
		// Port zero or empty means use requested to choose any freely available
		// port. Avoid this since it won't work with any configured clients,
		// can lead to serious loss of availability.
		fatalIf(errInvalidArgument, "Invalid port `%s`, please use `--address` to pick a specific port", portStr)
	}
	globalMinioHost = host

	// Check if requested port is available.
	fatalIf(checkPortAvailability(portStr), "Port unavailable %s", portStr)
	globalMinioPort = portStr

	// Check server syntax and exit in case of errors.
	// Done after globalMinioHost and globalMinioPort is set as parseStorageEndpoints()
	// depends on it.
	checkServerSyntax(c)

	// Disks to be used in server init.
	endpoints, err := parseStorageEndpoints(c.Args())
	fatalIf(err, "Unable to parse storage endpoints %s", c.Args())

	// Should exit gracefully if none of the endpoints passed
	// as command line args are local to this server.
	if !isAnyEndpointLocal(endpoints) {
		fatalIf(errInvalidArgument, "None of the disks passed as command line args are local to this server.")
	}

	// Sort endpoints for consistent ordering across multiple
	// nodes in a distributed setup. This is to avoid format.json
	// corruption if the disks aren't supplied in the same order
	// on all nodes.
	sort.Sort(byHostPath(endpoints))

	storageDisks, err := initStorageDisks(endpoints)
	fatalIf(err, "Unable to initialize storage disk(s).")

	// Cleanup objects that weren't successfully written into the namespace.
	fatalIf(houseKeeping(storageDisks), "Unable to purge temporary files.")

	// Initialize server config.
	initServerConfig(c)

	// First disk argument check if it is local.
	firstDisk := isLocalStorage(endpoints[0])

	// Check if endpoints are part of distributed setup.
	globalIsDistXL = isDistributedSetup(endpoints)

	// Configure server.
	srvConfig := serverCmdConfig{
		serverAddr:   serverAddr,
		endpoints:    endpoints,
		storageDisks: storageDisks,
	}

	// Configure server.
	handler, err := configureServerHandler(srvConfig)
	fatalIf(err, "Unable to configure one of server's RPC services.")

	// Set nodes for dsync for distributed setup.
	if globalIsDistXL {
		fatalIf(initDsyncNodes(endpoints), "Unable to initialize distributed locking")
	}

	// Initialize name space lock.
	initNSLock(globalIsDistXL)

	// Initialize a new HTTP server.
	apiServer := NewServerMux(serverAddr, handler)

	// If https.
	tls := isSSL()

	// Fetch endpoints which we are going to serve from.
	endPoints := finalizeEndpoints(tls, apiServer.Server)

	// Initialize local server address
	globalMinioAddr = getLocalAddress(srvConfig)

	// Initialize S3 Peers inter-node communication
	initGlobalS3Peers(endpoints)

	// Initialize Admin Peers inter-node communication
	initGlobalAdminPeers(endpoints)

	// Start server, automatically configures TLS if certs are available.
	go func(tls bool) {
		var lerr error
		cert, key := "", ""
		if tls {
			cert, key = mustGetCertFile(), mustGetKeyFile()
		}
		lerr = apiServer.ListenAndServe(cert, key)
		fatalIf(lerr, "Failed to start minio server.")
	}(tls)

	// Wait for formatting of disks.
	formattedDisks, err := waitForFormatDisks(firstDisk, endpoints, storageDisks)
	fatalIf(err, "formatting storage disks failed")

	// Once formatted, initialize object layer.
	newObject, err := newObjectLayer(formattedDisks)
	fatalIf(err, "intializing object layer failed")

	globalObjLayerMutex.Lock()
	globalObjectAPI = newObject
	globalObjLayerMutex.Unlock()

	// Prints the formatted startup message once object layer is initialized.
	printStartupMessage(endPoints)

	// Waits on the server.
	<-globalServiceDoneCh
}
Beispiel #10
0
// Make sure all the command line parameters are OK and exit in case of invalid parameters.
func checkServerSyntax(c *cli.Context) {
	serverAddr := c.String("address")

	host, portStr, err := net.SplitHostPort(serverAddr)
	fatalIf(err, "Unable to parse %s.", serverAddr)

	// Verify syntax for all the XL disks.
	disks := c.Args()
	endpoints, err := parseStorageEndpoints(disks)
	fatalIf(err, "Unable to parse storage endpoints %s", strings.Join(disks, " "))

	// Validate if endpoints follow the expected syntax.
	err = checkEndpointsSyntax(endpoints, disks)
	fatalIf(err, "Invalid endpoints found %s", strings.Join(disks, " "))

	// Validate for duplicate endpoints are supplied.
	err = checkDuplicateEndpoints(endpoints)
	fatalIf(err, "Duplicate entries in %s", strings.Join(disks, " "))

	if len(endpoints) > 1 {
		// Validate if we have sufficient disks for XL setup.
		err = checkSufficientDisks(endpoints)
		fatalIf(err, "Invalid number of disks supplied.")
	} else {
		// Validate if we have invalid disk for FS setup.
		if endpoints[0].Host != "" && endpoints[0].Scheme != "" {
			fatalIf(errInvalidArgument, "%s, FS setup expects a filesystem path", endpoints[0])
		}
	}

	if !isDistributedSetup(endpoints) {
		// for FS and singlenode-XL validation is done, return.
		return
	}

	// Rest of the checks applies only to distributed XL setup.
	if host != "" {
		// We are here implies --address host:port is passed, hence the user is trying
		// to run one minio process per export disk.
		if portStr == "" {
			fatalIf(errInvalidArgument, "Port missing, Host:Port should be specified for --address")
		}
		foundCnt := 0
		for _, ep := range endpoints {
			if ep.Host == serverAddr {
				foundCnt++
			}
		}
		if foundCnt == 0 {
			// --address host:port should be available in the XL disk list.
			fatalIf(errInvalidArgument, "%s is not available in %s", serverAddr, strings.Join(disks, " "))
		}
		if foundCnt > 1 {
			// --address host:port should match exactly one entry in the XL disk list.
			fatalIf(errInvalidArgument, "%s matches % entries in %s", serverAddr, foundCnt, strings.Join(disks, " "))
		}
	}

	tls := isSSL()
	for _, ep := range endpoints {
		if ep.Scheme == "https" && !tls {
			// Certificates should be provided for https configuration.
			fatalIf(errInvalidArgument, "Certificates not provided for https")
		}
	}
}
Beispiel #11
0
func serverMain(c *cli.Context) {
	checkServerSyntax(c)

	conf, err := initServer()
	fatalIf(err.Trace(), "Failed to read config for minio.", nil)

	certFile := c.GlobalString("cert")
	keyFile := c.GlobalString("key")
	if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
		fatalIf(probe.NewError(errInvalidArgument), "Both certificate and key are required to enable https.", nil)
	}

	minFreeDisk, err := parsePercentToInt(c.String("min-free-disk"), 64)
	fatalIf(err.Trace(c.String("min-free-disk")), "Invalid minium free disk size "+c.String("min-free-disk")+" passed.", nil)

	path := strings.TrimSpace(c.Args().Last())
	// Last argument is always path
	if _, err := os.Stat(path); err != nil {
		fatalIf(probe.NewError(err), "Unable to validate the path", nil)
	}
	tls := (certFile != "" && keyFile != "")
	serverConfig := cloudServerConfig{
		Address:         c.GlobalString("address"),
		AccessLog:       c.GlobalBool("enable-accesslog"),
		AccessKeyID:     conf.Credentials.AccessKeyID,
		SecretAccessKey: conf.Credentials.SecretAccessKey,
		Path:            path,
		MinFreeDisk:     minFreeDisk,
		TLS:             tls,
		CertFile:        certFile,
		KeyFile:         keyFile,
	}

	// configure API server.
	apiServer, err := configureAPIServer(serverConfig)
	errorIf(err.Trace(), "Failed to configure API server.", nil)

	Println("\nMinio Object Storage:")
	printServerMsg(apiServer)

	// configure Web server.
	webServer, err := configureWebServer(serverConfig)
	errorIf(err.Trace(), "Failed to configure Web server.", nil)

	Println("\nMinio Browser:")
	printServerMsg(webServer)

	Println("\nTo configure Minio Client:")
	if runtime.GOOS == "windows" {
		Println("    Download \"mc\" from https://dl.minio.io/client/mc/release/" + runtime.GOOS + "-" + runtime.GOARCH + "/mc.exe")
		Println("    $ mc.exe config host add myminio http://localhost:9000 " + conf.Credentials.AccessKeyID + " " + conf.Credentials.SecretAccessKey)
	} else {
		Println("    $ wget https://dl.minio.io/client/mc/release/" + runtime.GOOS + "-" + runtime.GOARCH + "/mc")
		Println("    $ chmod 755 mc")
		Println("    $ ./mc config host add myminio http://localhost:9000 " + conf.Credentials.AccessKeyID + " " + conf.Credentials.SecretAccessKey)
	}

	// Start server.
	err = minhttp.ListenAndServe(apiServer, webServer)
	errorIf(err.Trace(), "Failed to start the minio server.", nil)
}
Beispiel #12
0
func mainWatch(ctx *cli.Context) error {
	console.SetColor("Time", color.New(color.FgGreen))
	console.SetColor("Size", color.New(color.FgYellow))
	console.SetColor("EventType", color.New(color.FgCyan, color.Bold))
	console.SetColor("ObjectName", color.New(color.Bold))

	setGlobalsFromContext(ctx)
	checkWatchSyntax(ctx)

	args := ctx.Args()
	path := args[0]

	prefix := ctx.String("prefix")
	suffix := ctx.String("suffix")
	events := strings.Split(ctx.String("events"), ",")
	recursive := ctx.Bool("recursive")

	s3Client, pErr := newClient(path)
	if pErr != nil {
		fatalIf(pErr.Trace(), "Cannot parse the provided url.")
	}

	params := watchParams{
		recursive: recursive,
		accountID: fmt.Sprintf("%d", time.Now().Unix()),
		events:    events,
		prefix:    prefix,
		suffix:    suffix,
	}

	// Start watching on events
	wo, err := s3Client.Watch(params)
	fatalIf(err, "Cannot watch on the specified bucket.")

	trapCh := signalTrap(os.Interrupt, syscall.SIGTERM)

	// Initialize.. waitgroup to track the go-routine.
	wg := sync.WaitGroup{}

	// Increment wait group to wait subsequent routine.
	wg.Add(1)

	// Start routine to watching on events.
	go func() {
		defer wg.Done()

		// Wait for all events.
		for {
			select {
			case <-trapCh:
				// Signal received we are done.
				close(wo.done)
				return
			case event, ok := <-wo.Events():
				if !ok {
					return
				}
				msg := watchMessage{Event: event}
				printMsg(msg)
			case err, ok := <-wo.Errors():
				if !ok {
					return
				}
				errorIf(err, "Cannot watch on events.")
				return
			}
		}
	}()

	// Wait on the routine to be finished or exit.
	wg.Wait()

	return nil
}
Beispiel #13
0
func serverMain(c *cli.Context) {
	// check 'server' cli arguments.
	checkServerSyntax(c)

	// Initialize server config.
	initServerConfig(c)

	// Server address.
	serverAddress := c.String("address")

	host, port, _ := net.SplitHostPort(serverAddress)
	// If port empty, default to port '80'
	if port == "" {
		port = "80"
		// if SSL is enabled, choose port as "443" instead.
		if isSSL() {
			port = "443"
		}
	}

	// Check if requested port is available.
	checkPortAvailability(getPort(net.JoinHostPort(host, port)))

	// Save all command line args as export paths.
	exportPaths := c.Args()

	// Configure server.
	apiServer := configureServer(serverCmdConfig{
		serverAddr:  serverAddress,
		exportPaths: exportPaths,
	})

	// Credential.
	cred := serverConfig.GetCredential()

	// Region.
	region := serverConfig.GetRegion()

	// Print credentials and region.
	console.Println("\n" + cred.String() + "  " + colorMagenta("Region: ") + colorWhite(region))

	hosts, port := getListenIPs(apiServer) // get listen ips and port.
	tls := apiServer.TLSConfig != nil      // 'true' if TLS is enabled.

	console.Println("\nMinio Object Storage:")
	// Print api listen ips.
	printListenIPs(tls, hosts, port)

	console.Println("\nMinio Browser:")
	// Print browser listen ips.
	printListenIPs(tls, hosts, port)

	console.Println("\nTo configure Minio Client:")

	// Figure out right endpoint for 'mc'.
	endpoint := fmt.Sprintf("http://%s:%s", hosts[0], port)
	if tls {
		endpoint = fmt.Sprintf("https://%s:%s", hosts[0], port)
	}

	// Download 'mc' info.
	if runtime.GOOS == "windows" {
		console.Printf("    Download 'mc' from https://dl.minio.io/client/mc/release/%s-%s/mc.exe\n", runtime.GOOS, runtime.GOARCH)
		console.Printf("    $ mc.exe config host add myminio %s %s %s\n", endpoint, cred.AccessKeyID, cred.SecretAccessKey)
	} else {
		console.Printf("    $ wget https://dl.minio.io/client/mc/release/%s-%s/mc\n", runtime.GOOS, runtime.GOARCH)
		console.Printf("    $ chmod 755 mc\n")
		console.Printf("    $ ./mc config host add myminio %s %s %s\n", endpoint, cred.AccessKeyID, cred.SecretAccessKey)
	}

	// Start server.
	var err error
	// Configure TLS if certs are available.
	if isSSL() {
		err = apiServer.ListenAndServeTLS(mustGetCertFile(), mustGetKeyFile())
	} else {
		// Fallback to http.
		err = apiServer.ListenAndServe()
	}
	fatalIf(err, "Failed to start minio server.")
}
Beispiel #14
0
// serverMain handler called for 'minio server' command.
func serverMain(c *cli.Context) {
	// Check 'server' cli arguments.
	checkServerSyntax(c)

	// Initialize server config.
	initServerConfig(c)

	// If https.
	tls := isSSL()

	// Server address.
	serverAddress := c.String("address")

	// Check if requested port is available.
	port := getPort(serverAddress)
	err := checkPortAvailability(port)
	fatalIf(err, "Port unavailable %d", port)

	// Disks to be ignored in server init, to skip format healing.
	ignoredDisks := strings.Split(c.String("ignore-disks"), ",")

	// Disks to be used in server init.
	disks := c.Args()

	isDist := isDistributedSetup(disks)
	// Set nodes for dsync for distributed setup.
	if isDist {
		err = initDsyncNodes(disks, port)
		fatalIf(err, "Unable to initialize distributed locking")
	}

	// Initialize name space lock.
	initNSLock(isDist)

	// Configure server.
	srvConfig = serverCmdConfig{
		serverAddr:   serverAddress,
		disks:        disks,
		ignoredDisks: ignoredDisks,
	}

	// Initialize and monitor shutdown signals.
	err = initGracefulShutdown(os.Exit)
	fatalIf(err, "Unable to initialize graceful shutdown operation")

	// Configure server.
	handler := configureServerHandler(srvConfig)

	apiServer := NewServerMux(serverAddress, handler)

	// Fetch endpoints which we are going to serve from.
	endPoints := finalizeEndpoints(tls, &apiServer.Server)

	// Register generic callbacks.
	globalShutdownCBs.AddGenericCB(func() errCode {
		// apiServer.Stop()
		return exitSuccess
	})

	// Start server.
	// Configure TLS if certs are available.
	wait := make(chan struct{}, 1)
	go func(tls bool, wait chan<- struct{}) {
		fatalIf(func() error {
			defer func() {
				wait <- struct{}{}
			}()
			if tls {
				return apiServer.ListenAndServeTLS(mustGetCertFile(), mustGetKeyFile())
			} // Fallback to http.
			return apiServer.ListenAndServe()
		}(), "Failed to start minio server.")
	}(tls, wait)

	// Wait for formatting of disks.
	err = formatDisks(disks, ignoredDisks)
	if err != nil {
		// FIXME: call graceful exit
		errorIf(err, "formatting storage disks failed")
		return
	}

	// Once formatted, initialize object layer.
	newObject, err := newObjectLayer(disks, ignoredDisks)
	if err != nil {
		// FIXME: call graceful exit
		errorIf(err, "intializing object layer failed")
		return
	}

	// Prints the formatted startup message.
	printStartupMessage(endPoints)

	objLayerMutex.Lock()
	globalObjectAPI = newObject
	objLayerMutex.Unlock()

	// Waits on the server.
	<-wait
}