Просмотр исходного кода

Explicit datastore initialization using filename in config

Rod Hynes 11 лет назад
Родитель
Сommit
4d718dea04
5 измененных файлов с 47 добавлено и 14 удалено
  1. 7 1
      AndroidLibrary/psi/psi.go
  2. 7 1
      ConsoleClient/psiphonClient.go
  3. 5 0
      psiphon/config.go
  4. 27 11
      psiphon/dataStore.go
  5. 1 1
      psiphon/defaults.go

+ 7 - 1
AndroidLibrary/psi/psi.go

@@ -26,9 +26,10 @@ package psi
 
 import (
 	"fmt"
-	psiphon "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 	"log"
 	"sync"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 )
 
 type Listener interface {
@@ -60,6 +61,11 @@ func Start(configJson string, listener Listener) error {
 		return fmt.Errorf("error loading configuration file: %s", err)
 	}
 
+	err = psiphon.InitDataStore(config.DataStoreFilename)
+	if err != nil {
+		return fmt.Errorf("error initializing datastore: %s", err)
+	}
+
 	log.SetOutput(&logRelay{listener: listener})
 
 	controller = psiphon.NewController(config)

+ 7 - 1
ConsoleClient/psiphonClient.go

@@ -21,12 +21,13 @@ package main
 
 import (
 	"flag"
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 	"io/ioutil"
 	"log"
 	"os"
 	"os/signal"
 	"sync"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 )
 
 func main() {
@@ -46,6 +47,11 @@ func main() {
 		log.Fatalf("error processing configuration file: %s", err)
 	}
 
+	err = psiphon.InitDataStore(config.DataStoreFilename)
+	if err != nil {
+		return fmt.Errorf("error initializing datastore: %s", err)
+	}
+
 	if config.LogFilename != "" {
 		logFile, err := os.OpenFile(config.LogFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
 		if err != nil {

+ 5 - 0
psiphon/config.go

@@ -26,6 +26,7 @@ import (
 
 type Config struct {
 	LogFilename                        string
+	DataStoreFilename                  string
 	PropagationChannelId               string
 	SponsorId                          string
 	RemoteServerListUrl                string
@@ -72,6 +73,10 @@ func LoadConfig(configJson []byte) (*Config, error) {
 			errors.New("remote server list signature public key is missing from the configuration file"))
 	}
 
+	if config.DataStoreFilename == "" {
+		config.DataStoreFilename = DATA_STORE_FILENAME
+	}
+
 	if config.TunnelProtocol != "" {
 		if !Contains(SupportedTunnelProtocols, config.TunnelProtocol) {
 			return nil, ContextError(

+ 27 - 11
psiphon/dataStore.go

@@ -37,10 +37,15 @@ type dataStore struct {
 
 var singleton dataStore
 
-// initDataStore initializes the singleton instance of dataStore. This
+// InitDataStore initializes the singleton instance of dataStore. This
 // function uses a sync.Once and is safe for use by concurrent goroutines.
 // The underlying sql.DB connection pool is also safe.
-func initDataStore() {
+//
+// Note: the sync.Once was more useful when initDataStore was private and
+// called on-demand by the public functions below. Now we require an explicit
+// InitDataStore() call with the filename passed in. The on-demand calls
+// have been replaced by checkInitDataStore() to assert that Init was called.
+func InitDataStore(filename string) (err error) {
 	singleton.init.Do(func() {
 		const schema = `
         create table if not exists serverEntry
@@ -56,18 +61,29 @@ func initDataStore() {
              value text not null);
 		pragma journal_mode=WAL;
         `
-		db, err := sql.Open(
+		var db *sql.DB
+		db, err = sql.Open(
 			"sqlite3",
-			fmt.Sprintf("file:%s?cache=private&mode=rwc", DATA_STORE_FILENAME))
+			fmt.Sprintf("file:%s?cache=private&mode=rwc", filename))
 		if err != nil {
-			Fatal("initDataStore failed to open database: %s", err)
+			// Note: intending to set the err return value for InitDataStore
+			err = fmt.Errorf("initDataStore failed to open database: %s", err)
+			return
 		}
 		_, err = db.Exec(schema)
 		if err != nil {
-			Fatal("initDataStore failed to initialize schema: %s", err)
+			err = fmt.Errorf("initDataStore failed to initialize schema: %s", err)
+			return
 		}
 		singleton.db = db
 	})
+	return err
+}
+
+func checkInitDataStore() {
+	if singleton.db == nil {
+		panic("checkInitDataStore: datastore not initialized")
+	}
 }
 
 func canRetry(err error) bool {
@@ -81,7 +97,7 @@ func canRetry(err error) bool {
 // transactionWithRetry will retry a write transaction if sqlite3
 // reports a table is locked by another writer.
 func transactionWithRetry(updater func(*sql.Tx) error) error {
-	initDataStore()
+	checkInitDataStore()
 	for i := 0; i < 10; i++ {
 		if i > 0 {
 			// Delay on retry
@@ -205,7 +221,7 @@ type ServerEntryIterator struct {
 
 // NewServerEntryIterator creates a new NewServerEntryIterator
 func NewServerEntryIterator(region, protocol string) (iterator *ServerEntryIterator, err error) {
-	initDataStore()
+	checkInitDataStore()
 	iterator = &ServerEntryIterator{
 		region:   region,
 		protocol: protocol,
@@ -322,7 +338,7 @@ func makeServerEntryWhereClause(
 // least one server entry (for the specified region and/or protocol,
 // when not blank).
 func HasServerEntries(region, protocol string) bool {
-	initDataStore()
+	checkInitDataStore()
 	var count int
 	whereClause, whereParams := makeServerEntryWhereClause(region, protocol, nil)
 	query := "select count(*) from serverEntry" + whereClause
@@ -343,7 +359,7 @@ func HasServerEntries(region, protocol string) bool {
 // GetServerEntryIpAddresses returns an array containing
 // all stored server IP addresses.
 func GetServerEntryIpAddresses() (ipAddresses []string, err error) {
-	initDataStore()
+	checkInitDataStore()
 	ipAddresses = make([]string, 0)
 	rows, err := singleton.db.Query("select id from serverEntry;")
 	if err != nil {
@@ -382,7 +398,7 @@ func SetKeyValue(key, value string) error {
 // GetLastConnected retrieves a key/value pair. If not found,
 // it returns an empty string value.
 func GetKeyValue(key string) (value string, err error) {
-	initDataStore()
+	checkInitDataStore()
 	rows := singleton.db.QueryRow("select value from keyValue where key = ?;", key)
 	err = rows.Scan(&value)
 	if err == sql.ErrNoRows {

+ 1 - 1
psiphon/defaults.go

@@ -24,7 +24,7 @@ import (
 )
 
 const (
-	VERSION                                  = "0.0.3"
+	VERSION                                  = "0.0.4"
 	DATA_STORE_FILENAME                      = "psiphon.db"
 	CONNECTION_WORKER_POOL_SIZE              = 10
 	TUNNEL_POOL_SIZE                         = 1