func sweetdMain() error {
	sweetLog := sweetlog.New()
	log.SetOutput(os.Stdout)
	log.SetLevel(log.InfoLevel)
	log.AddHook(sweetLog)
	cfg, err := loadConfig()
	if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp {
		return nil
	} else if err != nil {
		return errors.Errorf("Failed parsing arguments: %v", err)
	}
	if cfg.Debug {
		log.SetLevel(log.DebugLevel)
		log.Info("Setting debug mode.")
	}
	log.Debug("Loaded config.")
	log.Infof("Version %s (commit %s)", Version, Commit)
	log.Infof("Built on %s", Date)
	if cfg.ShowVersion {
		return nil
	}
	if cfg.Profiling != nil && cfg.Profiling.Listen != "" {
		go func() {
			log.Infof("Starting profiling server on %v", cfg.Profiling.Listen)
			http.Handle("/", http.RedirectHandler("/debug/pprof", http.StatusSeeOther))
			err := http.ListenAndServe(cfg.Profiling.Listen, nil)
			if err != nil {
				log.Errorf("Could not run profiler: %v", err)
			}
		}()
	}
	sweetDB, err := sweetdb.Open(cfg.DataDir)
	if err != nil {
		return errors.Errorf("Could not open sweet.db: %v", err)
	}
	log.Infof("Opened sweet.db")
	defer func() {
		err := sweetDB.Close()
		if err != nil {
			log.Errorf("Could not close sweet.db: %v", err)
		} else {
			log.Info("Closed sweet.db.")
		}
	}()
	var net network.Network
	if cfg.Net.Interface != "" {
		net = network.NewWpaNetwork(&network.Config{
			Interface: cfg.Net.Interface,
			Logger:    log.WithField("system", "network"),
		})
		log.Info("Created network.")
	} else {
		net = network.NewMockNetwork()
		log.Info("Created mock network.")
	}
	err = net.Start()
	if err != nil {
		return errors.Errorf("Could not start network: %v", err)
	}
	defer func() {
		err = net.Stop()
		if err != nil {
			log.Errorf("Could not properly shut down network: %v", err)
		} else {
			log.Info("Stopped network.")
		}
	}()
	var u updater.Updater
	switch cfg.Updater {
	case "none":
		u = updater.NewNoopUpdater()
		log.Info("Created noop updater.")
	case "mender":
		u = updater.NewMenderUpdater(&updater.MenderUpdaterConfig{
			Logger: log.WithField("system", "updater"),
			DB:     sweetDB,
		})
		log.Info("Created Mender updater.")
	default:
		return errors.Errorf("Unknown updater type %v", cfg.Updater)
	}
	err = u.Setup()
	if err != nil {
		return errors.Errorf("unable to set up updater: %v", err)
	}
	var m machine.Machine
	switch cfg.Machine {
	case "raspberry":
		m = machine.NewDispenserMachine(&machine.DispenserMachineConfig{
			TouchPin:  cfg.Raspberry.TouchPin,
			MotorPin:  cfg.Raspberry.MotorPin,
			BuzzerPin: cfg.Raspberry.BuzzerPin,
		})
		log.Infof("Created Raspberry Pi machine on touch pin %v, motor pin %v and buzzer pin %v.",
			cfg.Raspberry.TouchPin, cfg.Raspberry.MotorPin, cfg.Raspberry.BuzzerPin)
	case "mock":
		m = machine.NewMockMachine(cfg.Mock.Listen)
		log.Info("Created a mock machine.")
	default:
		return errors.Errorf("Unknown machine type %v", cfg.Machine)
	}
	if err := m.Start(); err != nil {
		return errors.Errorf("Could not start machine: %v", err)
	}
	defer func() {
		err := m.Stop()
		if err != nil {
			log.Errorf("Could not properly stop machine: %v", err)
		} else {
			log.Infof("Stopped machine.")
		}
	}()
	t, err := tor.Start(nil, &tor.StartConf{
		ExePath:           cfg.Tor.Path,
		TempDataDirBase:   os.TempDir(),
		RetainTempDataDir: false,
		DebugWriter:       log.WithField("system", "tor").WriterLevel(log.DebugLevel),
	})
	if err != nil {
		return errors.Errorf("Could not start tor: %v", err)
	}
	log.Infof("Started Tor.")
	defer func() {
		t.StopProcessOnClose = true
		t.DeleteDataDirOnClose = true
		err := t.Close()
		if err != nil {
			log.Errorf("Could not properly stop Tor: %v", err)
		} else {
			log.Infof("Stopped Tor.")
		}
	}()
	nodeman := nodeman.New(&nodeman.Config{
		NodesDataDir: filepath.Join(cfg.DataDir, "nodes"),
		DB:           sweetDB,
		LogCreator: func(node string) nodeman.Logger {
			logger := log.WithField("system", "nodeman")
			if node != "" {
				logger = logger.WithField("node", node)
			}
			return logger
		},
	})
	pairingAdapter := &dispenser.PairingAdapter{}
	if cfg.Pairing.Interface != "" {
		pairingAdapter.Pairing, err = pairing.NewController(&pairing.BLEControllerConfig{
			Logger:    log.WithField("system", "pairing"),
			AdapterId: cfg.Pairing.Interface,
			Dispenser: pairingAdapter,
		})
		if err != nil {
			return errors.Errorf("unable to create BLE pairing controller: %v", err)
		}
		log.Infof("created BLE pairing controller")
	} else {
		pairingAdapter.Pairing = pairing.NewNoopController()
		log.Info("created no-op pairing controller as no interface was specified")
	}
	dispenser := dispenser.NewDispenser(&dispenser.Config{
		Nodeman:  nodeman,
		Machine:  m,
		DB:       sweetDB,
		Updater:  u,
		SweetLog: sweetLog,
		Logger:   log.WithField("system", "dispenser"),
		Tor:      t,
		Network:  net,
		Pairing:  pairingAdapter.Pairing,
	})
	pairingAdapter.Dispenser = dispenser
	log.Infof("created dispenser")
	err = pairingAdapter.Pairing.Start()
	if err != nil {
		return errors.Errorf("unable to start pairing controller: %v", err)
	}
	log.Info("started pairing controller")
	defer func() {
		err := pairingAdapter.Pairing.Stop()
		if err != nil {
			log.Errorf("unable to stop pairing controller: %v", err)
		} else {
			log.Info("stopped pairing controller")
		}
	}()
	go func() {
		signals := make(chan os.Signal, 1)
		signal.Notify(signals, os.Interrupt)
		sig := <-signals
		log.Info(sig)
		log.Info("Received an interrupt, stopping dispenser...")
		dispenser.Stop()
	}()
	err = dispenser.RunAndWait()
	if err != nil {
		return errors.Errorf("unable to run: %v", err)
	}
	return nil
}
sweetdMain is the true entry point for sweetd. This is required since defers
created in the top-level scope of a main method aren't executed if os.Exit() is called.
package main
import (
	"github.com/cretz/bine/tor"
	"github.com/jessevdk/go-flags"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"github.com/the-lightning-land/sweetd/dispenser"
	"github.com/the-lightning-land/sweetd/machine"
	"github.com/the-lightning-land/sweetd/network"
	"github.com/the-lightning-land/sweetd/nodeman"
	"github.com/the-lightning-land/sweetd/pairing"
	"github.com/the-lightning-land/sweetd/sweetdb"
	"github.com/the-lightning-land/sweetd/sweetlog"
	"github.com/the-lightning-land/sweetd/updater"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	_ "net/http/pprof"
)
var (
	Commit string
	Version string
	Date string
)