github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/siad/daemon.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/NebulousLabs/Sia/api"
    13  	"github.com/NebulousLabs/Sia/crypto"
    14  	"github.com/NebulousLabs/Sia/modules"
    15  	"github.com/NebulousLabs/Sia/modules/consensus"
    16  	"github.com/NebulousLabs/Sia/modules/explorer"
    17  	"github.com/NebulousLabs/Sia/modules/gateway"
    18  	"github.com/NebulousLabs/Sia/modules/host"
    19  	"github.com/NebulousLabs/Sia/modules/miner"
    20  	"github.com/NebulousLabs/Sia/modules/renter"
    21  	"github.com/NebulousLabs/Sia/modules/transactionpool"
    22  	"github.com/NebulousLabs/Sia/modules/wallet"
    23  	"github.com/NebulousLabs/Sia/profile"
    24  
    25  	"github.com/spf13/cobra"
    26  )
    27  
    28  // processNetAddr adds a ':' to a bare integer, so that it is a proper port
    29  // number.
    30  func processNetAddr(addr string) string {
    31  	_, err := strconv.Atoi(addr)
    32  	if err == nil {
    33  		return ":" + addr
    34  	}
    35  	return addr
    36  }
    37  
    38  // processModules makes the modules string lowercase to make checking if a
    39  // module in the string easier, and returns an error if the string contains an
    40  // invalid module character.
    41  func processModules(modules string) (string, error) {
    42  	modules = strings.ToLower(modules)
    43  	validModules := "cghmrtwe"
    44  	invalidModules := modules
    45  	for _, m := range validModules {
    46  		invalidModules = strings.Replace(invalidModules, string(m), "", 1)
    47  	}
    48  	if len(invalidModules) > 0 {
    49  		return "", errors.New("Unable to parse --modules flag, unrecognized or duplicate modules: " + invalidModules)
    50  	}
    51  	return modules, nil
    52  }
    53  
    54  // processConfig checks the configuration values and performs cleanup on
    55  // incorrect-but-allowed values.
    56  func processConfig(config Config) (Config, error) {
    57  	var err error
    58  	config.Siad.APIaddr = processNetAddr(config.Siad.APIaddr)
    59  	config.Siad.RPCaddr = processNetAddr(config.Siad.RPCaddr)
    60  	config.Siad.HostAddr = processNetAddr(config.Siad.HostAddr)
    61  	config.Siad.Modules, err = processModules(config.Siad.Modules)
    62  	if err != nil {
    63  		return Config{}, err
    64  	}
    65  	return config, nil
    66  }
    67  
    68  // startDaemonCmd uses the config parameters to start siad.
    69  func startDaemon(config Config) (err error) {
    70  	// Print a startup message.
    71  	fmt.Println("Loading...")
    72  	loadStart := time.Now()
    73  
    74  	// Create all of the modules.
    75  	i := 0
    76  	var g modules.Gateway
    77  	if strings.Contains(config.Siad.Modules, "g") {
    78  		i++
    79  		fmt.Printf("(%d/%d) Loading gateway...\n", i, len(config.Siad.Modules))
    80  		g, err = gateway.New(config.Siad.RPCaddr, filepath.Join(config.Siad.SiaDir, modules.GatewayDir))
    81  		if err != nil {
    82  			return err
    83  		}
    84  	}
    85  	var cs modules.ConsensusSet
    86  	if strings.Contains(config.Siad.Modules, "c") {
    87  		i++
    88  		fmt.Printf("(%d/%d) Loading consensus...\n", i, len(config.Siad.Modules))
    89  		cs, err = consensus.New(g, filepath.Join(config.Siad.SiaDir, modules.ConsensusDir))
    90  		if err != nil {
    91  			return err
    92  		}
    93  	}
    94  	var e modules.Explorer
    95  	if strings.Contains(config.Siad.Modules, "e") {
    96  		i++
    97  		fmt.Printf("(%d/%d) Loading explorer...\n", i, len(config.Siad.Modules))
    98  		e, err = explorer.New(cs, filepath.Join(config.Siad.SiaDir, modules.ExplorerDir))
    99  		if err != nil {
   100  			return err
   101  		}
   102  	}
   103  	var tpool modules.TransactionPool
   104  	if strings.Contains(config.Siad.Modules, "t") {
   105  		i++
   106  		fmt.Printf("(%d/%d) Loading transaction pool...\n", i, len(config.Siad.Modules))
   107  		tpool, err = transactionpool.New(cs, g, filepath.Join(config.Siad.SiaDir, modules.TransactionPoolDir))
   108  		if err != nil {
   109  			return err
   110  		}
   111  	}
   112  	var w modules.Wallet
   113  	if strings.Contains(config.Siad.Modules, "w") {
   114  		i++
   115  		fmt.Printf("(%d/%d) Loading wallet...\n", i, len(config.Siad.Modules))
   116  		w, err = wallet.New(cs, tpool, filepath.Join(config.Siad.SiaDir, modules.WalletDir))
   117  		if err != nil {
   118  			return err
   119  		}
   120  	}
   121  	var m modules.Miner
   122  	if strings.Contains(config.Siad.Modules, "m") {
   123  		i++
   124  		fmt.Printf("(%d/%d) Loading miner...\n", i, len(config.Siad.Modules))
   125  		m, err = miner.New(cs, tpool, w, filepath.Join(config.Siad.SiaDir, modules.MinerDir))
   126  		if err != nil {
   127  			return err
   128  		}
   129  	}
   130  	var h modules.Host
   131  	if strings.Contains(config.Siad.Modules, "h") {
   132  		i++
   133  		fmt.Printf("(%d/%d) Loading host...\n", i, len(config.Siad.Modules))
   134  		h, err = host.New(cs, tpool, w, config.Siad.HostAddr, filepath.Join(config.Siad.SiaDir, modules.HostDir))
   135  		if err != nil {
   136  			return err
   137  		}
   138  	}
   139  	var r modules.Renter
   140  	if strings.Contains(config.Siad.Modules, "r") {
   141  		i++
   142  		fmt.Printf("(%d/%d) Loading renter...\n", i, len(config.Siad.Modules))
   143  		r, err = renter.New(cs, w, tpool, filepath.Join(config.Siad.SiaDir, modules.RenterDir))
   144  		if err != nil {
   145  			return err
   146  		}
   147  	}
   148  	srv, err := api.NewServer(
   149  		config.Siad.APIaddr,
   150  		config.Siad.RequiredUserAgent,
   151  		cs,
   152  		e,
   153  		g,
   154  		h,
   155  		m,
   156  		r,
   157  		tpool,
   158  		w,
   159  	)
   160  	if err != nil {
   161  		return err
   162  	}
   163  
   164  	// Bootstrap to the network.
   165  	if !config.Siad.NoBootstrap && g != nil {
   166  		// connect to 3 random bootstrap nodes
   167  		perm, err := crypto.Perm(len(modules.BootstrapPeers))
   168  		if err != nil {
   169  			return err
   170  		}
   171  		for _, i := range perm[:3] {
   172  			go g.Connect(modules.BootstrapPeers[i])
   173  		}
   174  	}
   175  
   176  	// Print a 'startup complete' message.
   177  	startupTime := time.Since(loadStart)
   178  	fmt.Println("Finished loading in", startupTime.Seconds(), "seconds")
   179  
   180  	// Start serving api requests.
   181  	err = srv.Serve()
   182  	if err != nil {
   183  		return err
   184  	}
   185  	return nil
   186  }
   187  
   188  // startDaemonCmd is a passthrough function for startDaemon.
   189  func startDaemonCmd(cmd *cobra.Command, _ []string) {
   190  	// Create the profiling directory if profiling is enabled.
   191  	if globalConfig.Siad.Profile {
   192  		go profile.StartContinuousProfile(globalConfig.Siad.ProfileDir)
   193  	}
   194  
   195  	// Process the config variables after they are parsed by cobra.
   196  	config, err := processConfig(globalConfig)
   197  	if err != nil {
   198  		fmt.Fprintln(os.Stderr, err)
   199  		cmd.Usage()
   200  		os.Exit(exitCodeUsage)
   201  	}
   202  
   203  	// Start siad. startDaemon will only return when it is shutting down.
   204  	err = startDaemon(config)
   205  	if err != nil {
   206  		die(err)
   207  	}
   208  }