Skip to content

Commit

Permalink
rcbridge: Set cache dir via XDG_CACHE_HOME
Browse files Browse the repository at this point in the history
Due to how golang calls each packages' init() functions, it's impossible
to have a shared library function that runs before any of the init()s.
Instead, we'll set the XDG_CACHE_HOME environment variable. This has its
own problems because calling setenv() from the Kotlin side only affects
libc's global environ variable, which golang does not use (it maintains
its own copy of envp). This commit works around that by importing a new
envhack package that explicitly copies libc's environ to golang via CGO.

Fixes: #11

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
  • Loading branch information
chenxiaolong committed Jun 19, 2023
1 parent 2ef6188 commit e5069ea
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 4 deletions.
3 changes: 2 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ val rcbridge = tasks.register<Exec>("rcbridge") {
File(rcbridgeSrcDir, "go.mod"),
File(rcbridgeSrcDir, "go.sum"),
File(rcbridgeSrcDir, "rcbridge.go"),
File(File(rcbridgeSrcDir, "envhack"), "envhack.go"),
)
inputs.properties(
"android.defaultConfig.minSdk" to android.defaultConfig.minSdk,
Expand Down Expand Up @@ -341,7 +342,7 @@ fun checkBrackets(line: String) {

fun updateChangelogLinks(baseUrl: String) {
val file = File(rootDir, "CHANGELOG.md")
var regexStandaloneLink = Regex("\\[([^\\]]+)\\](?![\\(\\[])")
val regexStandaloneLink = Regex("\\[([^\\]]+)\\](?![\\(\\[])")
val regexAutoLink = Regex("(Issue|PR) #(\\d+)(?: @([\\w-]+))?")
val links = hashMapOf<LinkRef, String>()
var skipRemaining = false
Expand Down
11 changes: 10 additions & 1 deletion app/src/main/java/com/chiller3/rsaf/MainApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.chiller3.rsaf
import android.app.Application
import android.app.backup.BackupManager
import android.content.SharedPreferences
import android.system.Os
import android.util.Log
import com.chiller3.rsaf.binding.rcbridge.Rcbridge
import com.google.android.material.color.DynamicColors
Expand Down Expand Up @@ -45,7 +46,15 @@ class MainApplication : Application(), SharedPreferences.OnSharedPreferenceChang
}

private fun initRclone() {
Rcbridge.rbInit(File(cacheDir, "rclone").toString())
// Some of the rclone backend packages' init() functions set default values based on the
// value of config.GetCacheDir(). However, there's no way to call config.SetCacheDir() early
// enough that it'll be set before those init() functions. Instead, we'll set the
// XDG_CACHE_HOME environment variable to achieve the same effect. Environment variables set
// in libc's global environ variable are never read by golang, so rcbridge has an envhack
// package to explicitly copy env vars from C land to Go land.
Os.setenv("XDG_CACHE_HOME", cacheDir.path, true)

Rcbridge.rbInit()
RcloneConfig.init(this)
updateRcloneVerbosity()
}
Expand Down
37 changes: 37 additions & 0 deletions rcbridge/envhack/envhack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package envhack

/*
#include <unistd.h>
*/
import "C"
import (
"os"
"strings"
"unsafe"
)

func init() {
// Golang has its own internal copy of the environment variables. When using
// go as a shared library, the internal map never gets populated from
// _start()'s envp, so no environment variables are accessible. This
// terrible hack allows us to explicitly copy the environment variables from
// libc.

ptr := C.environ

for {
if *ptr == nil {
break
}

key_value := C.GoString(*ptr)
pieces := strings.SplitN(key_value, "=", 2)
if len(pieces) != 2 {
continue
}

os.Setenv(pieces[0], pieces[1])

ptr = (**C.char)(unsafe.Add(unsafe.Pointer(ptr), unsafe.Sizeof(ptr)))
}
}
6 changes: 4 additions & 2 deletions rcbridge/rcbridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
package rcbridge

import (
// This package's init() MUST run first
_ "rcbridge/envhack"

"context"
"io"
ioFs "io/fs"
Expand Down Expand Up @@ -65,9 +68,8 @@ var (
)

// Initialize global aspects of the library.
func RbInit(cacheDir string) {
func RbInit() {
librclone.Initialize()
config.SetCacheDir(cacheDir)

// Don't allow interactive password prompts
ci := fs.GetConfig(context.Background())
Expand Down

0 comments on commit e5069ea

Please sign in to comment.