github.com/Synthesix/Sia@v1.3.3-0.20180413141344-f863baeed3ca/cmd/siad/server.go (about)

     1  package main
     2  
     3  import (
     4  	"archive/zip"
     5  	"bytes"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"math/big"
    12  	"net"
    13  	"net/http"
    14  	"os"
    15  	"path"
    16  	"path/filepath"
    17  	"runtime"
    18  	"sort"
    19  	"strings"
    20  	"sync"
    21  	"syscall"
    22  	"time"
    23  
    24  	"github.com/Synthesix/Sia/build"
    25  	"github.com/Synthesix/Sia/modules"
    26  	"github.com/Synthesix/Sia/modules/consensus"
    27  	"github.com/Synthesix/Sia/modules/explorer"
    28  	"github.com/Synthesix/Sia/modules/gateway"
    29  	"github.com/Synthesix/Sia/modules/host"
    30  	"github.com/Synthesix/Sia/modules/miner"
    31  	"github.com/Synthesix/Sia/modules/renter"
    32  	"github.com/Synthesix/Sia/modules/transactionpool"
    33  	"github.com/Synthesix/Sia/modules/wallet"
    34  	"github.com/Synthesix/Sia/node/api"
    35  	"github.com/Synthesix/Sia/types"
    36  
    37  	"github.com/inconshreveable/go-update"
    38  	"github.com/julienschmidt/httprouter"
    39  	"github.com/kardianos/osext"
    40  )
    41  
    42  var errEmptyUpdateResponse = errors.New("API call to https://api.github.com/repos/NebulousLabs/Sia/releases/latest is returning an empty response")
    43  
    44  type (
    45  	// Server creates and serves a HTTP server that offers communication with a
    46  	// Sia API.
    47  	Server struct {
    48  		httpServer    *http.Server
    49  		listener      net.Listener
    50  		config        Config
    51  		moduleClosers []moduleCloser
    52  		api           http.Handler
    53  		mu            sync.Mutex
    54  	}
    55  
    56  	// moduleCloser defines a struct that closes modules, defined by a name and
    57  	// an underlying io.Closer.
    58  	moduleCloser struct {
    59  		name string
    60  		io.Closer
    61  	}
    62  
    63  	// SiaConstants is a struct listing all of the constants in use.
    64  	SiaConstants struct {
    65  		BlockFrequency         types.BlockHeight `json:"blockfrequency"`
    66  		BlockSizeLimit         uint64            `json:"blocksizelimit"`
    67  		ExtremeFutureThreshold types.Timestamp   `json:"extremefuturethreshold"`
    68  		FutureThreshold        types.Timestamp   `json:"futurethreshold"`
    69  		GenesisTimestamp       types.Timestamp   `json:"genesistimestamp"`
    70  		MaturityDelay          types.BlockHeight `json:"maturitydelay"`
    71  		MedianTimestampWindow  uint64            `json:"mediantimestampwindow"`
    72  		SiafundCount           types.Currency    `json:"siafundcount"`
    73  		SiafundPortion         *big.Rat          `json:"siafundportion"`
    74  		TargetWindow           types.BlockHeight `json:"targetwindow"`
    75  
    76  		InitialCoinbase uint64 `json:"initialcoinbase"`
    77  		MinimumCoinbase uint64 `json:"minimumcoinbase"`
    78  
    79  		RootTarget types.Target `json:"roottarget"`
    80  		RootDepth  types.Target `json:"rootdepth"`
    81  
    82  		MaxAdjustmentUp   *big.Rat `json:"maxadjustmentup"`
    83  		MaxAdjustmentDown *big.Rat `json:"maxadjustmentdown"`
    84  
    85  		SiacoinPrecision types.Currency `json:"siacoinprecision"`
    86  	}
    87  
    88  	// DaemonVersion holds the version information for siad
    89  	DaemonVersion struct {
    90  		Version     string `json:"version"`
    91  		GitRevision string `json:"gitrevision"`
    92  		BuildTime   string `json:"buildtime"`
    93  	}
    94  	// UpdateInfo indicates whether an update is available, and to what
    95  	// version.
    96  	UpdateInfo struct {
    97  		Available bool   `json:"available"`
    98  		Version   string `json:"version"`
    99  	}
   100  	// githubRelease represents some of the JSON returned by the GitHub release API
   101  	// endpoint. Only the fields relevant to updating are included.
   102  	githubRelease struct {
   103  		TagName string `json:"tag_name"`
   104  		Assets  []struct {
   105  			Name        string `json:"name"`
   106  			DownloadURL string `json:"browser_download_url"`
   107  		} `json:"assets"`
   108  	}
   109  )
   110  
   111  const (
   112  	// The developer key is used to sign updates and other important Sia-
   113  	// related information.
   114  	developerKey = `-----BEGIN PUBLIC KEY-----
   115  MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEAsoQHOEU6s/EqMDtw5HvA
   116  YPTUaBgnviMFbG3bMsRqSCD8ug4XJYh+Ik6WP0xgq+OPDehPiaXK8ghAtBiW1EJK
   117  mBRwlABXAzREZg8wRfG4l8Zj6ckAPJOgLn0jobXy6/SCQ+jZSWh4Y8DYr+LA3Mn3
   118  EOga7Jvhpc3fTZ232GBGJ1BobuNfRfYmwxSphv+T4vzIA3JUjVfa8pYZGIjh5XbJ
   119  5M8Lef0Xa9eqr6lYm5kQoOIXeOW56ImqI2BKg/I9NGw9phSPbwaFfy1V2kfHp5Xy
   120  DtKnyj/O9zDi+qUKjoIivnEoV+3DkioHUWv7Fpf7yx/9cPyckwvaBsTd9Cfp4uBx
   121  qJ5Qyv69VZQiD6DikNwgzjGbIjiLwfTObhInKZUoYl48yzgkR80ja5TW0SoidNvO
   122  4WTbWcLolOl522VarTs7wlgbq0Ad7yrNVnHzo447v2iT20ILH2oeAcZqvpcvRmTl
   123  U6uKoaVmBH3D3Y19dPluOjK53BrqfQ5L8RFli2wEJktPsi5fUTd4UI9BgnUieuDz
   124  S7h/VH9bv9ZVvyjpu/uVjdvaikT3zbIy9J6wS6uE5qPLPhI4B9HgbrQ03muDGpql
   125  gZrMiL3GdYrBiqpIbaWHfM0eMWEK3ZScUdtCgUXMMrkvaUJ4g9wEgbONFVVOMIV+
   126  YubIuzBFqug6WyxN/EAM/6Fss832AwVPcYM0NDTVGVdVplLMdN8YNjrYuaPngBCG
   127  e8QaTWtHzLujyBIkVdAHqfkRS65jp7JLLMx7jUA74/E/v+0cNew3Y1p2gt3iQH8t
   128  w93xn9IPUfQympc4h3KerP/Yn6P/qAh68jQkOiMMS+VbCq/BOn8Q3GbR+8rQ8dmk
   129  qVoGA7XrPQ6bymKBTghk2Ek+ZjxrpAoj0xYoYyzWf0kuxeOT8kAjlLLmfQ8pm75S
   130  QHLqH49FyfeETIU02rkw2oMOX/EYdJzZukHuouwbpKSElpRx+xTnaSemMJo+U7oX
   131  xVjma3Zynh9w12abnFWkZKtrxwXv7FCSzb0UZmMWUqWzCS03Rrlur21jp4q2Wl71
   132  Vt92xe5YbC/jbh386F1e/qGq6p+D1AmBynIpp/HE6fPsc9LWgJDDkREZcp7hthGW
   133  IdYPeP3CesFHnsZMueZRib0i7lNUkBSRneO1y/C9poNv1vOeTCNEE0jvhp/XOJuc
   134  yCQtrUSNALsvm7F+bnwP2F7K34k7MOlOgnTGqCqW+9WwBcjR44B0HI+YERCcRmJ8
   135  krBuVo9OBMV0cYBWpjo3UI9j3lHESCYhLnCz7SPap7C1yORc2ydJh+qjKqdLBHom
   136  t+JydcdJLbIG+kb3jB9QIIu5A4TlSGlHV6ewtxIWLS1473jEkITiVTt0Y5k+VLfW
   137  bwIDAQAB
   138  -----END PUBLIC KEY-----`
   139  )
   140  
   141  // version returns the version number of a non-LTS release. This assumes that
   142  // tag names will always be of the form "vX.Y.Z".
   143  func (r *githubRelease) version() string {
   144  	return strings.TrimPrefix(r.TagName, "v")
   145  }
   146  
   147  // byVersion sorts non-LTS releases by their version string, placing the highest
   148  // version number first.
   149  type byVersion []githubRelease
   150  
   151  func (rs byVersion) Len() int      { return len(rs) }
   152  func (rs byVersion) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] }
   153  func (rs byVersion) Less(i, j int) bool {
   154  	// we want the higher version number to reported as "less" so that it is
   155  	// placed first in the slice
   156  	return build.VersionCmp(rs[i].version(), rs[j].version()) >= 0
   157  }
   158  
   159  // latestRelease returns the latest non-LTS release, given a set of arbitrary
   160  // releases.
   161  func latestRelease(releases []githubRelease) (githubRelease, error) {
   162  	// filter the releases to exclude LTS releases
   163  	nonLTS := releases[:0]
   164  	for _, r := range releases {
   165  		if !strings.Contains(r.TagName, "lts") && build.IsVersion(r.version()) {
   166  			nonLTS = append(nonLTS, r)
   167  		}
   168  	}
   169  
   170  	// sort by version
   171  	sort.Sort(byVersion(nonLTS))
   172  
   173  	// return the latest release
   174  	if len(nonLTS) == 0 {
   175  		return githubRelease{}, errEmptyUpdateResponse
   176  	}
   177  	return nonLTS[0], nil
   178  }
   179  
   180  // fetchLatestRelease returns metadata about the most recent non-LTS GitHub
   181  // release.
   182  func fetchLatestRelease() (githubRelease, error) {
   183  	req, err := http.NewRequest("GET", "https://api.github.com/repos/NebulousLabs/Sia/releases", nil)
   184  	if err != nil {
   185  		return githubRelease{}, err
   186  	}
   187  	req.Header.Set("Accept", "application/vnd.github.v3+json")
   188  	resp, err := http.DefaultClient.Do(req)
   189  	if err != nil {
   190  		return githubRelease{}, err
   191  	}
   192  	defer resp.Body.Close()
   193  	var releases []githubRelease
   194  	err = json.NewDecoder(resp.Body).Decode(&releases)
   195  	if err != nil {
   196  		return githubRelease{}, err
   197  	}
   198  	return latestRelease(releases)
   199  }
   200  
   201  // updateToRelease updates siad and siac to the release specified. siac is
   202  // assumed to be in the same folder as siad.
   203  func updateToRelease(release githubRelease) error {
   204  	updateOpts := update.Options{
   205  		Verifier: update.NewRSAVerifier(),
   206  	}
   207  	err := updateOpts.SetPublicKeyPEM([]byte(developerKey))
   208  	if err != nil {
   209  		// should never happen
   210  		return err
   211  	}
   212  
   213  	binaryFolder, err := osext.ExecutableFolder()
   214  	if err != nil {
   215  		return err
   216  	}
   217  
   218  	// construct release filename
   219  	releaseName := fmt.Sprintf("Sia-%s-%s-%s.zip", release.TagName, runtime.GOOS, runtime.GOARCH)
   220  
   221  	// find release
   222  	var downloadURL string
   223  	for _, asset := range release.Assets {
   224  		if asset.Name == releaseName {
   225  			downloadURL = asset.DownloadURL
   226  			break
   227  		}
   228  	}
   229  	if downloadURL == "" {
   230  		return errors.New("couldn't find download URL for " + releaseName)
   231  	}
   232  
   233  	// download release archive
   234  	resp, err := http.Get(downloadURL)
   235  	if err != nil {
   236  		return err
   237  	}
   238  	// release should be small enough to store in memory (<10 MiB); use
   239  	// LimitReader to ensure we don't download more than 32 MiB
   240  	content, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<25))
   241  	resp.Body.Close()
   242  	if err != nil {
   243  		return err
   244  	}
   245  	r := bytes.NewReader(content)
   246  	z, err := zip.NewReader(r, r.Size())
   247  	if err != nil {
   248  		return err
   249  	}
   250  
   251  	// process zip, finding siad/siac binaries and signatures
   252  	for _, binary := range []string{"siad", "siac"} {
   253  		var binData io.ReadCloser
   254  		var signature []byte
   255  		var binaryName string // needed for TargetPath below
   256  		for _, zf := range z.File {
   257  			switch base := path.Base(zf.Name); base {
   258  			case binary, binary + ".exe":
   259  				binaryName = base
   260  				binData, err = zf.Open()
   261  				if err != nil {
   262  					return err
   263  				}
   264  				defer binData.Close()
   265  			case binary + ".sig", binary + ".exe.sig":
   266  				sigFile, err := zf.Open()
   267  				if err != nil {
   268  					return err
   269  				}
   270  				defer sigFile.Close()
   271  				signature, err = ioutil.ReadAll(sigFile)
   272  				if err != nil {
   273  					return err
   274  				}
   275  			}
   276  		}
   277  		if binData == nil {
   278  			return errors.New("could not find " + binary + " binary")
   279  		} else if signature == nil {
   280  			return errors.New("could not find " + binary + " signature")
   281  		}
   282  
   283  		// apply update
   284  		updateOpts.Signature = signature
   285  		updateOpts.TargetMode = 0775 // executable
   286  		updateOpts.TargetPath = filepath.Join(binaryFolder, binaryName)
   287  		err = update.Apply(binData, updateOpts)
   288  		if err != nil {
   289  			return err
   290  		}
   291  	}
   292  
   293  	return nil
   294  }
   295  
   296  // daemonUpdateHandlerGET handles the API call that checks for an update.
   297  func (srv *Server) daemonUpdateHandlerGET(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
   298  	release, err := fetchLatestRelease()
   299  	if err != nil {
   300  		api.WriteError(w, api.Error{Message: "Failed to fetch latest release: " + err.Error()}, http.StatusInternalServerError)
   301  		return
   302  	}
   303  	latestVersion := release.TagName[1:] // delete leading 'v'
   304  	api.WriteJSON(w, UpdateInfo{
   305  		Available: build.VersionCmp(latestVersion, build.Version) > 0,
   306  		Version:   latestVersion,
   307  	})
   308  }
   309  
   310  // daemonUpdateHandlerPOST handles the API call that updates siad and siac.
   311  // There is no safeguard to prevent "updating" to the same release, so callers
   312  // should always check the latest version via daemonUpdateHandlerGET first.
   313  // TODO: add support for specifying version to update to.
   314  func (srv *Server) daemonUpdateHandlerPOST(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
   315  	release, err := fetchLatestRelease()
   316  	if err != nil {
   317  		api.WriteError(w, api.Error{Message: "Failed to fetch latest release: " + err.Error()}, http.StatusInternalServerError)
   318  		return
   319  	}
   320  	err = updateToRelease(release)
   321  	if err != nil {
   322  		if rerr := update.RollbackError(err); rerr != nil {
   323  			api.WriteError(w, api.Error{Message: "Serious error: Failed to rollback from bad update: " + rerr.Error()}, http.StatusInternalServerError)
   324  		} else {
   325  			api.WriteError(w, api.Error{Message: "Failed to apply update: " + err.Error()}, http.StatusInternalServerError)
   326  		}
   327  		return
   328  	}
   329  	api.WriteSuccess(w)
   330  }
   331  
   332  // debugConstantsHandler prints a json file containing all of the constants.
   333  func (srv *Server) daemonConstantsHandler(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
   334  	sc := SiaConstants{
   335  		BlockFrequency:         types.BlockFrequency,
   336  		BlockSizeLimit:         types.BlockSizeLimit,
   337  		ExtremeFutureThreshold: types.ExtremeFutureThreshold,
   338  		FutureThreshold:        types.FutureThreshold,
   339  		GenesisTimestamp:       types.GenesisTimestamp,
   340  		MaturityDelay:          types.MaturityDelay,
   341  		MedianTimestampWindow:  types.MedianTimestampWindow,
   342  		SiafundCount:           types.SiafundCount,
   343  		SiafundPortion:         types.SiafundPortion,
   344  		TargetWindow:           types.TargetWindow,
   345  
   346  		InitialCoinbase: types.InitialCoinbase,
   347  		MinimumCoinbase: types.MinimumCoinbase,
   348  
   349  		RootTarget: types.RootTarget,
   350  		RootDepth:  types.RootDepth,
   351  
   352  		MaxAdjustmentUp:   types.MaxAdjustmentUp,
   353  		MaxAdjustmentDown: types.MaxAdjustmentDown,
   354  
   355  		SiacoinPrecision: types.SiacoinPrecision,
   356  	}
   357  
   358  	api.WriteJSON(w, sc)
   359  }
   360  
   361  // daemonVersionHandler handles the API call that requests the daemon's version.
   362  func (srv *Server) daemonVersionHandler(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
   363  	api.WriteJSON(w, DaemonVersion{Version: build.Version, GitRevision: build.GitRevision, BuildTime: build.BuildTime})
   364  }
   365  
   366  // daemonStopHandler handles the API call to stop the daemon cleanly.
   367  func (srv *Server) daemonStopHandler(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
   368  	// can't write after we stop the server, so lie a bit.
   369  	api.WriteSuccess(w)
   370  
   371  	// need to flush the response before shutting down the server
   372  	f, ok := w.(http.Flusher)
   373  	if !ok {
   374  		panic("Server does not support flushing")
   375  	}
   376  	f.Flush()
   377  
   378  	if err := srv.Close(); err != nil {
   379  		build.Critical(err)
   380  	}
   381  }
   382  
   383  func (srv *Server) daemonHandler(password string) http.Handler {
   384  	router := httprouter.New()
   385  
   386  	router.GET("/daemon/constants", srv.daemonConstantsHandler)
   387  	router.GET("/daemon/version", srv.daemonVersionHandler)
   388  	router.GET("/daemon/update", srv.daemonUpdateHandlerGET)
   389  	router.POST("/daemon/update", srv.daemonUpdateHandlerPOST)
   390  	router.GET("/daemon/stop", api.RequirePassword(srv.daemonStopHandler, password))
   391  
   392  	return router
   393  }
   394  
   395  // apiHandler handles all calls to the API. If the ready flag is not set, this
   396  // will return an error. Otherwise it will serve the api.
   397  func (srv *Server) apiHandler(w http.ResponseWriter, r *http.Request) {
   398  	srv.mu.Lock()
   399  	isReady := srv.api != nil
   400  	srv.mu.Unlock()
   401  	if !isReady {
   402  		api.WriteError(w, api.Error{Message: "siad is not ready. please wait for siad to finish loading."}, http.StatusServiceUnavailable)
   403  		return
   404  	}
   405  	srv.api.ServeHTTP(w, r)
   406  }
   407  
   408  // NewServer creates a new net.http server listening on bindAddr.  Only the
   409  // /daemon/ routes are registered by this func, additional routes can be
   410  // registered later by calling serv.mux.Handle.
   411  func NewServer(config Config) (*Server, error) {
   412  	// Process the config variables after they are parsed by cobra.
   413  	config, err := processConfig(config)
   414  	if err != nil {
   415  		return nil, err
   416  	}
   417  	// Create the listener for the server
   418  	l, err := net.Listen("tcp", config.Siad.APIaddr)
   419  	if err != nil {
   420  		if isAddrInUseErr(err) {
   421  			return nil, fmt.Errorf("%v; are you running another instance of siad?", err.Error())
   422  		}
   423  
   424  		return nil, err
   425  	}
   426  
   427  	// Create the Server
   428  	mux := http.NewServeMux()
   429  	srv := &Server{
   430  		listener: l,
   431  		httpServer: &http.Server{
   432  			Handler: mux,
   433  
   434  			// set reasonable timeout windows for requests, to prevent the Sia API
   435  			// server from leaking file descriptors due to slow, disappearing, or
   436  			// unreliable API clients.
   437  
   438  			// ReadTimeout defines the maximum amount of time allowed to fully read
   439  			// the request body. This timeout is applied to every handler in the
   440  			// server.
   441  			ReadTimeout: time.Minute * 5,
   442  
   443  			// ReadHeaderTimeout defines the amount of time allowed to fully read the
   444  			// request headers.
   445  			ReadHeaderTimeout: time.Minute * 2,
   446  
   447  			// IdleTimeout defines the maximum duration a HTTP Keep-Alive connection
   448  			// the API is kept open with no activity before closing.
   449  			IdleTimeout: time.Minute * 5,
   450  		},
   451  		config: config,
   452  	}
   453  
   454  	// Register siad routes
   455  	mux.Handle("/daemon/", api.RequireUserAgent(srv.daemonHandler(config.APIPassword), config.Siad.RequiredUserAgent))
   456  	mux.HandleFunc("/", srv.apiHandler)
   457  
   458  	return srv, nil
   459  }
   460  
   461  // isAddrInUseErr checks if the error corresponds to syscall.EADDRINUSE
   462  func isAddrInUseErr(err error) bool {
   463  	if opErr, ok := err.(*net.OpError); ok {
   464  		if syscallErr, ok := opErr.Err.(*os.SyscallError); ok {
   465  			return syscallErr.Err == syscall.EADDRINUSE
   466  		}
   467  	}
   468  	return false
   469  }
   470  
   471  // loadModules loads the modules defined by the server's config and makes their
   472  // API routes available.
   473  func (srv *Server) loadModules() error {
   474  	// Create the server and start serving daemon routes immediately.
   475  	fmt.Printf("(0/%d) Loading siad...\n", len(srv.config.Siad.Modules))
   476  
   477  	// Initialize the Sia modules
   478  	i := 0
   479  	var err error
   480  	var g modules.Gateway
   481  	if strings.Contains(srv.config.Siad.Modules, "g") {
   482  		i++
   483  		fmt.Printf("(%d/%d) Loading gateway...\n", i, len(srv.config.Siad.Modules))
   484  		g, err = gateway.New(srv.config.Siad.RPCaddr, !srv.config.Siad.NoBootstrap, filepath.Join(srv.config.Siad.SiaDir, modules.GatewayDir))
   485  		if err != nil {
   486  			return err
   487  		}
   488  		srv.moduleClosers = append(srv.moduleClosers, moduleCloser{name: "gateway", Closer: g})
   489  	}
   490  	var cs modules.ConsensusSet
   491  	if strings.Contains(srv.config.Siad.Modules, "c") {
   492  		i++
   493  		fmt.Printf("(%d/%d) Loading consensus...\n", i, len(srv.config.Siad.Modules))
   494  		cs, err = consensus.New(g, !srv.config.Siad.NoBootstrap, filepath.Join(srv.config.Siad.SiaDir, modules.ConsensusDir))
   495  		if err != nil {
   496  			return err
   497  		}
   498  		srv.moduleClosers = append(srv.moduleClosers, moduleCloser{name: "consensus", Closer: cs})
   499  	}
   500  	var e modules.Explorer
   501  	if strings.Contains(srv.config.Siad.Modules, "e") {
   502  		i++
   503  		fmt.Printf("(%d/%d) Loading explorer...\n", i, len(srv.config.Siad.Modules))
   504  		e, err = explorer.New(cs, filepath.Join(srv.config.Siad.SiaDir, modules.ExplorerDir))
   505  		if err != nil {
   506  			return err
   507  		}
   508  		srv.moduleClosers = append(srv.moduleClosers, moduleCloser{name: "explorer", Closer: e})
   509  	}
   510  	var tpool modules.TransactionPool
   511  	if strings.Contains(srv.config.Siad.Modules, "t") {
   512  		i++
   513  		fmt.Printf("(%d/%d) Loading transaction pool...\n", i, len(srv.config.Siad.Modules))
   514  		tpool, err = transactionpool.New(cs, g, filepath.Join(srv.config.Siad.SiaDir, modules.TransactionPoolDir))
   515  		if err != nil {
   516  			return err
   517  		}
   518  		srv.moduleClosers = append(srv.moduleClosers, moduleCloser{name: "transaction pool", Closer: tpool})
   519  	}
   520  	var w modules.Wallet
   521  	if strings.Contains(srv.config.Siad.Modules, "w") {
   522  		i++
   523  		fmt.Printf("(%d/%d) Loading wallet...\n", i, len(srv.config.Siad.Modules))
   524  		w, err = wallet.New(cs, tpool, filepath.Join(srv.config.Siad.SiaDir, modules.WalletDir))
   525  		if err != nil {
   526  			return err
   527  		}
   528  		srv.moduleClosers = append(srv.moduleClosers, moduleCloser{name: "wallet", Closer: w})
   529  	}
   530  	var m modules.Miner
   531  	if strings.Contains(srv.config.Siad.Modules, "m") {
   532  		i++
   533  		fmt.Printf("(%d/%d) Loading miner...\n", i, len(srv.config.Siad.Modules))
   534  		m, err = miner.New(cs, tpool, w, filepath.Join(srv.config.Siad.SiaDir, modules.MinerDir))
   535  		if err != nil {
   536  			return err
   537  		}
   538  		srv.moduleClosers = append(srv.moduleClosers, moduleCloser{name: "miner", Closer: m})
   539  	}
   540  	var h modules.Host
   541  	if strings.Contains(srv.config.Siad.Modules, "h") {
   542  		i++
   543  		fmt.Printf("(%d/%d) Loading host...\n", i, len(srv.config.Siad.Modules))
   544  		h, err = host.New(cs, tpool, w, srv.config.Siad.HostAddr, filepath.Join(srv.config.Siad.SiaDir, modules.HostDir))
   545  		if err != nil {
   546  			return err
   547  		}
   548  		srv.moduleClosers = append(srv.moduleClosers, moduleCloser{name: "host", Closer: h})
   549  	}
   550  	var r modules.Renter
   551  	if strings.Contains(srv.config.Siad.Modules, "r") {
   552  		i++
   553  		fmt.Printf("(%d/%d) Loading renter...\n", i, len(srv.config.Siad.Modules))
   554  		r, err = renter.New(g, cs, w, tpool, filepath.Join(srv.config.Siad.SiaDir, modules.RenterDir))
   555  		if err != nil {
   556  			return err
   557  		}
   558  		srv.moduleClosers = append(srv.moduleClosers, moduleCloser{name: "renter", Closer: r})
   559  	}
   560  
   561  	// Create the Sia API
   562  	a := api.New(
   563  		srv.config.Siad.RequiredUserAgent,
   564  		srv.config.APIPassword,
   565  		cs,
   566  		e,
   567  		g,
   568  		h,
   569  		m,
   570  		r,
   571  		tpool,
   572  		w,
   573  	)
   574  
   575  	// connect the API to the server
   576  	srv.mu.Lock()
   577  	srv.api = a
   578  	srv.mu.Unlock()
   579  
   580  	// Attempt to auto-unlock the wallet using the SIA_WALLET_PASSWORD env variable
   581  	if password := os.Getenv("SIA_WALLET_PASSWORD"); password != "" {
   582  		fmt.Println("Sia Wallet Password found, attempting to auto-unlock wallet")
   583  		if err := unlockWallet(w, password); err != nil {
   584  			fmt.Println("Auto-unlock failed.")
   585  		} else {
   586  			fmt.Println("Auto-unlock successful.")
   587  		}
   588  	}
   589  
   590  	return nil
   591  }
   592  
   593  // Serve starts the HTTP server
   594  func (srv *Server) Serve() error {
   595  	// The server will run until an error is encountered or the listener is
   596  	// closed, via either the Close method or the signal handling above.
   597  	// Closing the listener will result in the benign error handled below.
   598  	err := srv.httpServer.Serve(srv.listener)
   599  	if err != nil && !strings.HasSuffix(err.Error(), "use of closed network connection") {
   600  		return err
   601  	}
   602  	return nil
   603  }
   604  
   605  // Close closes the Server's listener, causing the HTTP server to shut down.
   606  func (srv *Server) Close() error {
   607  	var errs []error
   608  	// Close the listener, which will cause Server.Serve() to return.
   609  	if err := srv.listener.Close(); err != nil {
   610  		errs = append(errs, err)
   611  	}
   612  	// Close all of the modules in reverse order
   613  	for i := len(srv.moduleClosers) - 1; i >= 0; i-- {
   614  		m := srv.moduleClosers[i]
   615  		fmt.Printf("Closing %v...\n", m.name)
   616  		if err := m.Close(); err != nil {
   617  			errs = append(errs, err)
   618  		}
   619  	}
   620  
   621  	return build.JoinErrors(errs, "\n")
   622  }