瀏覽代碼

Change data store disk configuration from filename to data directory and temp directory -- enables working configuration of library on Android.

Rod Hynes 11 年之前
父節點
當前提交
c1f6d6cb0c

+ 4 - 2
AndroidApp/README.md

@@ -16,11 +16,13 @@ Status
 Native libraries
 --------------------------------------------------------------------------------
 
-`app\src\main\jniLibs\<platform>\libtun2socks.so` is built from the Psiphon fork of badvpn. Source code is here: [https://bitbucket.org/psiphon/psiphon-circumvention-system/src/default/Android/badvpn/](https://bitbucket.org/psiphon/psiphon-circumvention-system/src/default/Android/badvpn/). The source was modified to change the package name to `ca.psiphon.psibot`.
+`app/src/main/jniLibs/<platform>/libtun2socks.so` is built from the Psiphon fork of badvpn. Source code is here: [https://bitbucket.org/psiphon/psiphon-circumvention-system/src/default/Android/badvpn/](https://bitbucket.org/psiphon/psiphon-circumvention-system/src/default/Android/badvpn/). The source was modified to change the package name to `ca.psiphon.psibot`.
 
 Psiphon Android Library and config file
 --------------------------------------------------------------------------------
 
 Uses the [Psiphon Android Library](../AndroidLibrary/README.md).
 
-`app\src\main\res\raw\psiphon_config_stub` and its placeholder values must be replaced with `app\src\main\res\raw\psiphon_config` and valid configuration values.
+* `app/src/main/res/raw/psiphon_config_stub` and its placeholder values must be replaced with `app\src\main\res\raw\psiphon_config` and valid configuration values.
+
+* Install the Android Library shared object binary at `app/src/main/jniLibs/armeabi-v7a/libgojni.so`.

+ 5 - 3
AndroidApp/app/src/main/java/ca/psiphon/psibot/Psiphon.java

@@ -25,7 +25,6 @@ import android.net.VpnService;
 import org.json.JSONException;
 import org.json.JSONObject;
 
-import java.io.File;
 import java.io.IOException;
 import java.util.HashSet;
 import java.util.Set;
@@ -120,8 +119,11 @@ public class Psiphon extends Psi.PsiphonProvider.Stub {
             json.put("BindToDeviceDnsServer", dnsResolver);
         }
 
-        String dataStoreFilename = new File(mVpnService.getFilesDir(), "psiphon.db").getPath();
-        json.put("DataStoreFilename", dataStoreFilename);
+        // On Android, these directories must be set to the app private storage area.
+        // The Psiphon library won't be able to use its current working directory
+        // and the standard temporary directories do not exist.
+        json.put("DataStoreDirectory", mVpnService.getFilesDir());
+        json.put("DataStoreTempDirectory", mVpnService.getCacheDir());
 
         return json.toString();
     }

+ 2 - 1
AndroidApp/app/src/main/res/raw/psiphon_config_stub

@@ -3,7 +3,8 @@
     "SponsorId" : "<placeholder>",
     "RemoteServerListUrl" : "<placeholder>",
     "RemoteServerListSignaturePublicKey" : "<placeholder>",
-    "DataStoreFilename" : "/data/data/ca.psiphon.psibot/psiphon.db",
+    "DataStoreDirectory" : "",
+    "DataStoreTempDirectory" : "",
     "LogFilename" : "",
     "LocalHttpProxyPort" : 0,
     "LocalSocksProxyPort" : 0,

+ 2 - 2
AndroidLibrary/README.md

@@ -52,10 +52,10 @@ NOTE: may change after Go 1.4 is released.
 Using
 --------------------------------------------------------------------------------
 
-1. Build from source or use the provided shared object binary and Java source files
+1. Build from source or use the [provided shared object binary](TODO: link to release) and Java source files
 1. Add Go/Java integration files `java_golang/go/*.java` to your `$src/go`
 1. Add `java_psi/go/psi/Psi.java` to your `$src/go/psi`
-1. Add `gojni.so
+1. Add `libgojni.so` to your Android app
 
 NOTE: may change to Psiphon-specific library name and init.
 

+ 1 - 1
AndroidLibrary/psi/psi.go

@@ -65,7 +65,7 @@ func Start(configJson string, provider PsiphonProvider) error {
 		return fmt.Errorf("error loading configuration file: %s", err)
 	}
 
-	err = psiphon.InitDataStore(config.DataStoreFilename)
+	err = psiphon.InitDataStore(config)
 	if err != nil {
 		return fmt.Errorf("error initializing datastore: %s", err)
 	}

+ 1 - 1
ConsoleClient/psiphonClient.go

@@ -47,7 +47,7 @@ func main() {
 		log.Fatalf("error processing configuration file: %s", err)
 	}
 
-	err = psiphon.InitDataStore(config.DataStoreFilename)
+	err = psiphon.InitDataStore(config)
 	if err != nil {
 		log.Fatalf("error initializing datastore: %s", err)
 	}

+ 2 - 1
README.md

@@ -28,7 +28,8 @@ Setup
         "SponsorId" : "<placeholder>",
         "RemoteServerListUrl" : "<placeholder>",
         "RemoteServerListSignaturePublicKey" : "<placeholder>",
-        "DataStoreFilename" : "",
+        "DataStoreDirectory" : "",
+        "DataStoreTempDirectory" : "",
         "LogFilename" : "",
         "LocalHttpProxyPort" : 0,
         "LocalSocksProxyPort" : 0,

+ 8 - 3
psiphon/config.go

@@ -22,11 +22,13 @@ package psiphon
 import (
 	"encoding/json"
 	"errors"
+	"os"
 )
 
 type Config struct {
 	LogFilename                        string
-	DataStoreFilename                  string
+	DataStoreDirectory                 string
+	DataStoreTempDirectory             string
 	PropagationChannelId               string
 	SponsorId                          string
 	RemoteServerListUrl                string
@@ -73,8 +75,11 @@ 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.DataStoreDirectory == "" {
+		config.DataStoreDirectory, err = os.Getwd()
+		if err != nil {
+			return nil, ContextError(err)
+		}
 	}
 
 	if config.TunnelProtocol != "" {

+ 1 - 1
psiphon/controller.go

@@ -182,7 +182,7 @@ func (controller *Controller) runTunnels() {
 			break
 		}
 		// TODO: replace polling with signal
-		timeout := time.After(1 * time.Second)
+		timeout := time.After(5 * time.Second)
 		select {
 		case <-timeout:
 		case <-controller.shutdownBroadcast:

+ 28 - 14
psiphon/dataStore.go

@@ -24,6 +24,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"path/filepath"
 	"strings"
 	"sync"
 	"time"
@@ -46,9 +47,32 @@ var singleton dataStore
 // 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) {
+func InitDataStore(config *Config) (err error) {
 	singleton.init.Do(func() {
-		const schema = `
+		filename := filepath.Join(config.DataStoreDirectory, DATA_STORE_FILENAME)
+		var db *sql.DB
+		db, err = sql.Open(
+			"sqlite3",
+			fmt.Sprintf("file:%s?cache=private&mode=rwc", filename))
+		if err != nil {
+			// Note: intending to set the err return value for InitDataStore
+			err = fmt.Errorf("initDataStore failed to open database: %s", err)
+			return
+		}
+		initialization := "pragma journal_mode=WAL;\n"
+		if config.DataStoreTempDirectory != "" {
+			// On some platforms (e.g., Android), the standard temporary directories expected
+			// by sqlite (see unixGetTempname in aggregate sqlite3.c) may not be present.
+			// In that case, sqlite tries to use the current working directory; but this may
+			// be "/" (again, on Android) which is not writable.
+			// Instead of setting the process current working directory from this library,
+			// use the deprecated temp_store_directory pragma to force use of a specified
+			// temporary directory: https://www.sqlite.org/pragma.html#pragma_temp_store_directory.
+			// TODO: is there another way to restrict writing of temporary files? E.g. temp_store=3?
+			initialization += fmt.Sprintf(
+				"pragma temp_store_directory=\"%s\";\n", config.DataStoreDirectory)
+		}
+		initialization += `
         create table if not exists serverEntry
             (id text not null primary key,
              rank integer not null unique,
@@ -61,20 +85,10 @@ func InitDataStore(filename string) (err error) {
         create table if not exists keyValue
             (key text not null primary key,
              value text not null);
-        pragma journal_mode=WAL;
         `
-		var db *sql.DB
-		db, err = sql.Open(
-			"sqlite3",
-			fmt.Sprintf("file:%s?cache=private&mode=rwc", filename))
-		if err != nil {
-			// 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)
+		_, err = db.Exec(initialization)
 		if err != nil {
-			err = fmt.Errorf("initDataStore failed to initialize schema: %s", err)
+			err = fmt.Errorf("initDataStore failed to initialize: %s", err)
 			return
 		}
 		singleton.db = db

+ 2 - 2
psiphon/defaults.go

@@ -24,7 +24,7 @@ import (
 )
 
 const (
-	VERSION                                  = "0.0.4"
+	VERSION                                  = "0.0.5"
 	DATA_STORE_FILENAME                      = "psiphon.db"
 	CONNECTION_WORKER_POOL_SIZE              = 10
 	TUNNEL_POOL_SIZE                         = 1
@@ -37,7 +37,7 @@ const (
 	PORT_FORWARD_FAILURE_THRESHOLD           = 10
 	HTTP_PROXY_ORIGIN_SERVER_TIMEOUT         = 15 * time.Second
 	HTTP_PROXY_MAX_IDLE_CONNECTIONS_PER_HOST = 50
-	FETCH_REMOTE_SERVER_LIST_TIMEOUT         = 5 * time.Second
+	FETCH_REMOTE_SERVER_LIST_TIMEOUT         = 10 * time.Second
 	FETCH_REMOTE_SERVER_LIST_RETRY_TIMEOUT   = 5 * time.Second
 	FETCH_REMOTE_SERVER_LIST_STALE_TIMEOUT   = 6 * time.Hour
 	PSIPHON_API_CLIENT_SESSION_ID_LENGTH     = 16