1// SPDX-License-Identifier: ISC
2// Copyright (c) 2014-2020 Bitmark Inc.
3// Use of this source code is governed by an ISC
4// license that can be found in the LICENSE file.
5
6package announce
7
8import (
9	"path"
10	"sync"
11
12	"github.com/bitmark-inc/bitmarkd/announce/broadcast"
13	"github.com/bitmark-inc/bitmarkd/announce/domain"
14	"github.com/bitmark-inc/bitmarkd/announce/parameter"
15	"github.com/bitmark-inc/bitmarkd/announce/receptor"
16	"github.com/bitmark-inc/bitmarkd/announce/rpc"
17	"github.com/bitmark-inc/bitmarkd/background"
18	"github.com/bitmark-inc/bitmarkd/fault"
19	"github.com/bitmark-inc/bitmarkd/messagebus"
20	"github.com/bitmark-inc/logger"
21)
22
23const (
24	logCategory = "announce"
25)
26
27// file for storing saves peers
28const backupFile = "peers.json"
29
30// globals for background process
31type announcerData struct {
32	sync.RWMutex // to allow locking
33
34	log *logger.L
35
36	// RPC interface
37	rpcs rpc.RPC
38
39	// Receptor interface
40	receptors receptor.Receptor
41
42	backupFile string
43
44	// data for thread
45	brdc background.Process
46
47	domain background.Process
48
49	// for background
50	background *background.T
51
52	// set once during initialise
53	initialised bool
54}
55
56// global data
57var globalData announcerData
58
59// Initialise - set up the announcement system
60// pass a fully qualified domain for root node list
61// or empty string for no root nodes
62func Initialise(domainName, cacheDirectory string, f func(string) ([]string, error)) error {
63	globalData.Lock()
64	defer globalData.Unlock()
65
66	var err error
67
68	// no need to start if already started
69	if globalData.initialised {
70		return fault.AlreadyInitialised
71	}
72
73	globalData.log = logger.New(logCategory)
74	globalData.log.Info("starting…")
75
76	globalData.receptors = receptor.New(globalData.log)
77	globalData.backupFile = path.Join(cacheDirectory, backupFile)
78
79	globalData.log.Info("start restoring backup data…")
80	if err := receptor.Restore(globalData.backupFile, globalData.receptors); err != nil {
81		globalData.log.Errorf("fail to restore backup data: %s", err.Error())
82	}
83
84	globalData.rpcs = rpc.New()
85
86	globalData.domain, err = domain.New(
87		globalData.log,
88		domainName,
89		globalData.receptors,
90		f,
91	)
92	if nil != err {
93		return err
94	}
95
96	globalData.brdc = broadcast.New(
97		globalData.log,
98		globalData.receptors,
99		globalData.rpcs,
100		parameter.InitialiseInterval,
101		parameter.PollingInterval,
102	)
103
104	// all data initialised
105	globalData.initialised = true
106
107	// start background processes
108	globalData.log.Info("start background…")
109
110	processes := background.Processes{
111		globalData.domain, globalData.brdc,
112	}
113
114	globalData.background = background.Start(processes, messagebus.Bus.Announce.Chan())
115
116	return nil
117}
118
119// Finalise - stop all background tasks
120func Finalise() error {
121	if !globalData.initialised {
122		return fault.NotInitialised
123	}
124
125	globalData.log.Info("shutting down…")
126	globalData.log.Flush()
127
128	// stop background
129	globalData.background.Stop()
130
131	// release message bus
132	messagebus.Bus.Announce.Release()
133
134	globalData.log.Info("start backing up peer data…")
135	if err := receptor.Backup(globalData.backupFile, globalData.receptors.Connectable()); err != nil {
136		globalData.log.Errorf("fail to backup peer data: %s", err.Error())
137	}
138
139	// finally...
140	globalData.initialised = false
141
142	globalData.log.Info("finished")
143	globalData.log.Flush()
144
145	return nil
146}
147